summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.gtest.mk5
-rw-r--r--compiler/Android.mk3
-rw-r--r--compiler/cfi_test.h2
-rw-r--r--compiler/common_compiler_test.cc55
-rw-r--r--compiler/compiled_method.cc92
-rw-r--r--compiler/compiled_method.h98
-rw-r--r--compiler/compiled_method_test.cc120
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc2
-rw-r--r--compiler/dex/quick/codegen_util.cc3
-rw-r--r--compiler/dex/quick/dex_file_method_inliner.cc9
-rw-r--r--compiler/dex/quick/quick_cfi_test.cc1
-rw-r--r--compiler/dex/quick/ralloc_util.cc1
-rw-r--r--compiler/dex/quick/x86/quick_assemble_x86_test.cc1
-rw-r--r--compiler/driver/compiled_method_storage.cc269
-rw-r--r--compiler/driver/compiled_method_storage.h117
-rw-r--r--compiler/driver/compiled_method_storage_test.cc160
-rw-r--r--compiler/driver/compiler_driver-inl.h14
-rw-r--r--compiler/driver/compiler_driver.cc62
-rw-r--r--compiler/driver/compiler_driver.h134
-rw-r--r--compiler/driver/compiler_options.cc157
-rw-r--r--compiler/driver/compiler_options.h66
-rw-r--r--compiler/dwarf/dwarf_test.cc8
-rw-r--r--compiler/dwarf/headers.h13
-rw-r--r--compiler/dwarf/writer.h8
-rw-r--r--compiler/elf_writer_debug.cc4
-rw-r--r--compiler/image_test.cc7
-rw-r--r--compiler/image_writer.cc20
-rw-r--r--compiler/image_writer.h8
-rw-r--r--compiler/jit/jit_compiler.cc68
-rw-r--r--compiler/jit/jit_compiler.h4
-rw-r--r--compiler/jni/quick/jni_compiler.cc2
-rw-r--r--compiler/linker/arm/relative_patcher_arm_base.cc14
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2_test.cc30
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.cc2
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64_test.cc35
-rw-r--r--compiler/linker/relative_patcher_test.h10
-rw-r--r--compiler/oat_test.cc7
-rw-r--r--compiler/oat_writer.cc163
-rw-r--r--compiler/oat_writer.h15
-rw-r--r--compiler/optimizing/builder.cc93
-rw-r--r--compiler/optimizing/builder.h6
-rw-r--r--compiler/optimizing/code_generator.cc18
-rw-r--r--compiler/optimizing/code_generator.h7
-rw-r--r--compiler/optimizing/code_generator_arm.cc146
-rw-r--r--compiler/optimizing/code_generator_arm.h6
-rw-r--r--compiler/optimizing/code_generator_arm64.cc7
-rw-r--r--compiler/optimizing/code_generator_arm64.h6
-rw-r--r--compiler/optimizing/code_generator_mips.cc98
-rw-r--r--compiler/optimizing/code_generator_mips.h6
-rw-r--r--compiler/optimizing/code_generator_mips64.cc51
-rw-r--r--compiler/optimizing/code_generator_mips64.h6
-rw-r--r--compiler/optimizing/code_generator_x86.cc40
-rw-r--r--compiler/optimizing/code_generator_x86.h6
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc23
-rw-r--r--compiler/optimizing/code_generator_x86_64.h6
-rw-r--r--compiler/optimizing/constant_folding.cc53
-rw-r--r--compiler/optimizing/constant_folding_test.cc232
-rw-r--r--compiler/optimizing/induction_var_analysis.cc74
-rw-r--r--compiler/optimizing/induction_var_analysis.h22
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc4
-rw-r--r--compiler/optimizing/induction_var_range.cc10
-rw-r--r--compiler/optimizing/induction_var_range_test.cc2
-rw-r--r--compiler/optimizing/inliner.cc26
-rw-r--r--compiler/optimizing/inliner.h4
-rw-r--r--compiler/optimizing/intrinsics_mips.cc230
-rw-r--r--compiler/optimizing/intrinsics_mips.h83
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc193
-rw-r--r--compiler/optimizing/load_store_elimination.cc2
-rw-r--r--compiler/optimizing/nodes.cc44
-rw-r--r--compiler/optimizing/nodes.h35
-rw-r--r--compiler/optimizing/optimizing_compiler.cc24
-rw-r--r--compiler/optimizing/reference_type_propagation.cc16
-rw-r--r--compiler/optimizing/sharpening.cc142
-rw-r--r--compiler/optimizing/sharpening.h58
-rw-r--r--compiler/utils/arm/assembler_arm.cc21
-rw-r--r--compiler/utils/arm/assembler_arm.h73
-rw-r--r--compiler/utils/arm/assembler_arm32.cc32
-rw-r--r--compiler/utils/arm/assembler_arm32.h7
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc206
-rw-r--r--compiler/utils/arm/assembler_thumb2.h39
-rw-r--r--compiler/utils/arm/assembler_thumb2_test.cc310
-rw-r--r--compiler/utils/assembler.h4
-rw-r--r--compiler/utils/dedupe_set-inl.h253
-rw-r--r--compiler/utils/dedupe_set.h144
-rw-r--r--compiler/utils/dedupe_set_test.cc65
-rw-r--r--compiler/utils/x86/assembler_x86.cc17
-rw-r--r--compiler/utils/x86/assembler_x86.h2
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc13
-rw-r--r--dex2oat/dex2oat.cc376
-rw-r--r--dexdump/dexdump.cc17
-rw-r--r--runtime/Android.mk5
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S102
-rw-r--r--runtime/arch/mips/context_mips.cc4
-rw-r--r--runtime/arch/mips/context_mips.h9
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S2
-rw-r--r--runtime/art_method-inl.h6
-rw-r--r--runtime/art_method.cc144
-rw-r--r--runtime/art_method.h24
-rw-r--r--runtime/base/allocator.h9
-rw-r--r--runtime/base/dchecked_vector.h4
-rw-r--r--runtime/base/hash_set.h26
-rw-r--r--runtime/base/hash_set_test.cc21
-rw-r--r--runtime/base/timing_logger.cc2
-rw-r--r--runtime/class_linker.cc72
-rw-r--r--runtime/class_linker.h4
-rw-r--r--runtime/class_linker_test.cc4
-rw-r--r--runtime/debugger.cc29
-rw-r--r--runtime/dex_file.cc96
-rw-r--r--runtime/dex_file.h44
-rw-r--r--runtime/dex_file_test.cc1
-rw-r--r--runtime/dex_file_verifier.cc10
-rw-r--r--runtime/dex_file_verifier.h30
-rw-r--r--runtime/dex_instruction.h1
-rw-r--r--runtime/exception_test.cc23
-rw-r--r--runtime/gc/accounting/bitmap.cc2
-rw-r--r--runtime/gc/accounting/space_bitmap-inl.h2
-rw-r--r--runtime/gc/collector/concurrent_copying-inl.h110
-rw-r--r--runtime/gc/collector/concurrent_copying.cc288
-rw-r--r--runtime/gc/collector/concurrent_copying.h5
-rw-r--r--runtime/gc/collector/mark_sweep.cc4
-rw-r--r--runtime/gc/heap.cc11
-rw-r--r--runtime/image.cc8
-rw-r--r--runtime/instrumentation.cc10
-rw-r--r--runtime/jit/jit.cc2
-rw-r--r--runtime/jit/jit.h2
-rw-r--r--runtime/jit/jit_code_cache.cc382
-rw-r--r--runtime/jit/jit_code_cache.h126
-rw-r--r--runtime/jit/jit_instrumentation.cc43
-rw-r--r--runtime/jit/profiling_info.cc32
-rw-r--r--runtime/jit/profiling_info.h29
-rw-r--r--runtime/length_prefixed_array.h42
-rw-r--r--runtime/linear_alloc.cc4
-rw-r--r--runtime/linear_alloc.h4
-rw-r--r--runtime/mirror/class-inl.h8
-rw-r--r--runtime/mirror/class.cc2
-rw-r--r--runtime/mirror/object-inl.h85
-rw-r--r--runtime/mirror/object.h23
-rw-r--r--runtime/mirror/object_array-inl.h2
-rw-r--r--runtime/mirror/object_test.cc15
-rw-r--r--runtime/native/java_lang_Class.cc2
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/oat_file.cc22
-rw-r--r--runtime/oat_file.h6
-rw-r--r--runtime/oat_quick_method_header.h18
-rw-r--r--runtime/proxy_test.cc4
-rw-r--r--runtime/quick/inline_method_analyser.cc1
-rw-r--r--runtime/quick_exception_handler.cc7
-rw-r--r--runtime/read_barrier-inl.h8
-rw-r--r--runtime/stack.cc30
-rw-r--r--runtime/stack.h4
-rw-r--r--runtime/stride_iterator.h2
-rw-r--r--runtime/thread_list.cc43
-rw-r--r--runtime/thread_list.h40
-rw-r--r--runtime/type_lookup_table.cc131
-rw-r--r--runtime/type_lookup_table.h162
-rw-r--r--runtime/type_lookup_table_test.cc86
-rw-r--r--runtime/utils.cc39
-rw-r--r--runtime/utils.h31
-rw-r--r--runtime/verifier/method_verifier.cc37
-rw-r--r--test/004-ReferenceMap/stack_walk_refmap_jni.cc7
-rw-r--r--test/449-checker-bce/src/Main.java5
-rw-r--r--test/538-checker-embed-constants/src/Main.java25
-rw-r--r--test/539-checker-arm64-encodable-immediates/info.txt2
-rw-r--r--test/539-checker-arm64-encodable-immediates/src/Main.java52
-rw-r--r--test/541-regression-inlined-deopt/expected.txt (renamed from test/539-checker-arm64-encodable-immediates/expected.txt)0
-rw-r--r--test/541-regression-inlined-deopt/info.txt4
-rw-r--r--test/541-regression-inlined-deopt/smali/TestCase.smali55
-rw-r--r--test/541-regression-inlined-deopt/src/Main.java36
-rw-r--r--test/542-inline-trycatch/expected.txt0
-rw-r--r--test/542-inline-trycatch/info.txt1
-rw-r--r--test/542-inline-trycatch/src/Main.java178
-rw-r--r--test/542-unresolved-access-check/expected.txt0
-rw-r--r--test/542-unresolved-access-check/info.txt1
-rw-r--r--test/542-unresolved-access-check/src/Main.java104
-rw-r--r--test/542-unresolved-access-check/src/p1/InP1.java93
-rw-r--r--test/542-unresolved-access-check/src/p1/OtherInP1.java32
-rw-r--r--test/542-unresolved-access-check/src/p1/PlaceHolder.java24
-rwxr-xr-xtest/955-lambda-smali/run2
-rwxr-xr-xtest/960-default-smali/build25
-rw-r--r--test/960-default-smali/info.txt11
-rwxr-xr-xtest/960-default-smali/run6
-rwxr-xr-xtest/961-default-iface-resolution-generated/build27
-rwxr-xr-xtest/961-default-iface-resolution-generated/run6
-rwxr-xr-xtest/962-iface-static/build21
-rwxr-xr-xtest/962-iface-static/run6
-rw-r--r--test/962-iface-static/smali/Displayer.smali2
-rw-r--r--test/962-iface-static/smali/iface.smali2
-rwxr-xr-xtest/963-default-range-smali/build21
-rwxr-xr-xtest/963-default-range-smali/run6
-rwxr-xr-xtest/964-default-iface-init-generated/build21
-rwxr-xr-xtest/964-default-iface-init-generated/run6
-rwxr-xr-xtest/964-default-iface-init-generated/util-src/generate_smali.py2
-rw-r--r--test/Android.run-test.mk3
-rw-r--r--test/Lookup/A.java17
-rw-r--r--test/Lookup/AB.java17
-rw-r--r--test/Lookup/C.java17
-rwxr-xr-xtest/etc/default-build107
-rwxr-xr-xtest/etc/run-test-jar15
-rwxr-xr-xtest/run-all-tests58
-rwxr-xr-xtest/run-test12
-rwxr-xr-xtest/utils/python/generate_smali_main.py (renamed from test/960-default-smali/util-src/generate_smali.py)2
-rw-r--r--tools/libcore_failures.txt7
-rwxr-xr-xtools/run-jdwp-tests.sh10
-rwxr-xr-xtools/run-libcore-tests.sh7
-rwxr-xr-xtools/setup-buildbot-device.sh7
205 files changed, 6670 insertions, 2127 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 6295e1527b..ff41736a35 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -29,6 +29,7 @@ GTEST_DEX_DIRECTORIES := \
GetMethodSignature \
Instrumentation \
Interfaces \
+ Lookup \
Main \
MultiDex \
MultiDexModifiedSecondary \
@@ -78,6 +79,7 @@ ART_GTEST_proxy_test_DEX_DEPS := Interfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
ART_GTEST_stub_test_DEX_DEPS := AllFields
ART_GTEST_transaction_test_DEX_DEPS := Transaction
+ART_GTEST_type_lookup_table_test_DEX_DEPS := Lookup
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
@@ -220,6 +222,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \
runtime/reference_table_test.cc \
runtime/thread_pool_test.cc \
runtime/transaction_test.cc \
+ runtime/type_lookup_table_test.cc \
runtime/utf_test.cc \
runtime/utils_test.cc \
runtime/verifier/method_verifier_test.cc \
@@ -230,6 +233,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \
runtime/jni_internal_test.cc \
runtime/proxy_test.cc \
runtime/reflection_test.cc \
+ compiler/compiled_method_test.cc \
compiler/dex/gvn_dead_code_elimination_test.cc \
compiler/dex/global_value_numbering_test.cc \
compiler/dex/local_value_numbering_test.cc \
@@ -237,6 +241,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \
compiler/dex/mir_optimization_test.cc \
compiler/dex/type_inference_test.cc \
compiler/dwarf/dwarf_test.cc \
+ compiler/driver/compiled_method_storage_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
compiler/image_test.cc \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 17f9d122a7..e74a68f608 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -54,6 +54,7 @@ LIBART_COMPILER_SRC_FILES := \
dex/verification_results.cc \
dex/vreg_analysis.cc \
dex/quick_compiler_callbacks.cc \
+ driver/compiled_method_storage.cc \
driver/compiler_driver.cc \
driver/compiler_options.cc \
driver/dex_compilation_unit.cc \
@@ -88,6 +89,7 @@ LIBART_COMPILER_SRC_FILES := \
optimizing/primitive_type_propagation.cc \
optimizing/reference_type_propagation.cc \
optimizing/register_allocator.cc \
+ optimizing/sharpening.cc \
optimizing/side_effects_analysis.cc \
optimizing/ssa_builder.cc \
optimizing/ssa_liveness_analysis.cc \
@@ -153,6 +155,7 @@ LIBART_COMPILER_SRC_FILES_mips := \
dex/quick/mips/utility_mips.cc \
jni/quick/mips/calling_convention_mips.cc \
optimizing/code_generator_mips.cc \
+ optimizing/intrinsics_mips.cc \
utils/mips/assembler_mips.cc \
utils/mips/managed_register_mips.cc \
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index 5e345dbf61..6fd457599f 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -51,7 +51,7 @@ class CFITest : public dwarf::DwarfTest {
dwarf::WriteDebugFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8),
initial_opcodes, kCFIFormat, &debug_frame_data_);
std::vector<uintptr_t> debug_frame_patches;
- dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi,
+ dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), ArrayRef<const uint8_t>(actual_cfi),
kCFIFormat, &debug_frame_data_, &debug_frame_patches);
ReformatCfi(Objdump(false, "-W"), &lines);
// Pretty-print assembly.
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 58a2f96cd9..c37cecaeac 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -54,22 +54,22 @@ void CommonCompilerTest::MakeExecutable(ArtMethod* method) {
method->GetDexMethodIndex()));
}
if (compiled_method != nullptr) {
- const SwapVector<uint8_t>* code = compiled_method->GetQuickCode();
- uint32_t code_size = code->size();
+ ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
+ uint32_t code_size = code.size();
CHECK_NE(0u, code_size);
- const SwapVector<uint8_t>* vmap_table = compiled_method->GetVmapTable();
- uint32_t vmap_table_offset = vmap_table->empty() ? 0u
- : sizeof(OatQuickMethodHeader) + vmap_table->size();
- const SwapVector<uint8_t>* mapping_table = compiled_method->GetMappingTable();
- bool mapping_table_used = mapping_table != nullptr && !mapping_table->empty();
- size_t mapping_table_size = mapping_table_used ? mapping_table->size() : 0U;
+ ArrayRef<const uint8_t> vmap_table = compiled_method->GetVmapTable();
+ uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+ : sizeof(OatQuickMethodHeader) + vmap_table.size();
+ ArrayRef<const uint8_t> mapping_table = compiled_method->GetMappingTable();
+ bool mapping_table_used = !mapping_table.empty();
+ size_t mapping_table_size = mapping_table.size();
uint32_t mapping_table_offset = !mapping_table_used ? 0u
- : sizeof(OatQuickMethodHeader) + vmap_table->size() + mapping_table_size;
- const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap();
- bool gc_map_used = gc_map != nullptr && !gc_map->empty();
- size_t gc_map_size = gc_map_used ? gc_map->size() : 0U;
+ : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size;
+ ArrayRef<const uint8_t> gc_map = compiled_method->GetGcMap();
+ bool gc_map_used = !gc_map.empty();
+ size_t gc_map_size = gc_map.size();
uint32_t gc_map_offset = !gc_map_used ? 0u
- : sizeof(OatQuickMethodHeader) + vmap_table->size() + mapping_table_size + gc_map_size;
+ : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size + gc_map_size;
OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset,
compiled_method->GetFrameSizeInBytes(),
compiled_method->GetCoreSpillMask(),
@@ -77,25 +77,30 @@ void CommonCompilerTest::MakeExecutable(ArtMethod* method) {
header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
- size_t size = sizeof(method_header) + code_size + vmap_table->size() + mapping_table_size +
- gc_map_size;
- size_t code_offset = compiled_method->AlignCode(size - code_size);
- size_t padding = code_offset - (size - code_size);
- chunk->reserve(padding + size);
+ const size_t max_padding = GetInstructionSetAlignment(compiled_method->GetInstructionSet());
+ const size_t size =
+ gc_map_size + mapping_table_size + vmap_table.size() + sizeof(method_header) + code_size;
+ chunk->reserve(size + max_padding);
chunk->resize(sizeof(method_header));
memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
- chunk->insert(chunk->begin(), vmap_table->begin(), vmap_table->end());
+ chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
if (mapping_table_used) {
- chunk->insert(chunk->begin(), mapping_table->begin(), mapping_table->end());
+ chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
}
if (gc_map_used) {
- chunk->insert(chunk->begin(), gc_map->begin(), gc_map->end());
+ chunk->insert(chunk->begin(), gc_map.begin(), gc_map.end());
}
+ chunk->insert(chunk->end(), code.begin(), code.end());
+ CHECK_EQ(chunk->size(), size);
+ const void* unaligned_code_ptr = chunk->data() + (size - code_size);
+ size_t offset = dchecked_integral_cast<size_t>(reinterpret_cast<uintptr_t>(unaligned_code_ptr));
+ size_t padding = compiled_method->AlignCode(offset) - offset;
+ // Make sure no resizing takes place.
+ CHECK_GE(chunk->capacity(), chunk->size() + padding);
chunk->insert(chunk->begin(), padding, 0);
- chunk->insert(chunk->end(), code->begin(), code->end());
- CHECK_EQ(padding + size, chunk->size());
- const void* code_ptr = &(*chunk)[code_offset];
- MakeExecutable(code_ptr, code->size());
+ const void* code_ptr = reinterpret_cast<const uint8_t*>(unaligned_code_ptr) + padding;
+ CHECK_EQ(code_ptr, static_cast<const void*>(chunk->data() + (chunk->size() - code_size)));
+ MakeExecutable(code_ptr, code.size());
const void* method_code = CompiledMethod::CodePointer(code_ptr,
compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index 74ef35e740..9551d2298b 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -15,27 +15,22 @@
*/
#include "compiled_method.h"
+
+#include "driver/compiled_method_storage.h"
#include "driver/compiler_driver.h"
+#include "utils/swap_space.h"
namespace art {
CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code, bool owns_code_array)
- : compiler_driver_(compiler_driver), instruction_set_(instruction_set),
- owns_code_array_(owns_code_array), quick_code_(nullptr) {
- if (owns_code_array_) {
- // If we are supposed to own the code, don't deduplicate it.
- quick_code_ = new SwapVector<uint8_t>(quick_code.begin(), quick_code.end(),
- compiler_driver_->GetSwapSpaceAllocator());
- } else {
- quick_code_ = compiler_driver_->DeduplicateCode(quick_code);
- }
+ const ArrayRef<const uint8_t>& quick_code)
+ : compiler_driver_(compiler_driver),
+ instruction_set_(instruction_set),
+ quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)) {
}
CompiledCode::~CompiledCode() {
- if (owns_code_array_) {
- delete quick_code_;
- }
+ compiler_driver_->GetCompiledMethodStorage()->ReleaseCode(quick_code_);
}
bool CompiledCode::operator==(const CompiledCode& rhs) const {
@@ -104,59 +99,28 @@ const void* CompiledCode::CodePointer(const void* code_pointer,
}
}
-const std::vector<uint32_t>& CompiledCode::GetOatdataOffsetsToCompliledCodeOffset() const {
- CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size());
- return oatdata_offsets_to_compiled_code_offset_;
-}
-
-void CompiledCode::AddOatdataOffsetToCompliledCodeOffset(uint32_t offset) {
- oatdata_offsets_to_compiled_code_offset_.push_back(offset);
-}
-
CompiledMethod::CompiledMethod(CompilerDriver* driver,
InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code,
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const SrcMapElem>& src_mapping_table,
const ArrayRef<const uint8_t>& mapping_table,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<const LinkerPatch>& patches)
- : CompiledCode(driver, instruction_set, quick_code, !driver->DedupeEnabled()),
- owns_arrays_(!driver->DedupeEnabled()),
+ : CompiledCode(driver, instruction_set, quick_code),
frame_size_in_bytes_(frame_size_in_bytes), core_spill_mask_(core_spill_mask),
fp_spill_mask_(fp_spill_mask),
- patches_(patches.begin(), patches.end(), driver->GetSwapSpaceAllocator()) {
- if (owns_arrays_) {
- if (src_mapping_table == nullptr) {
- src_mapping_table_ = new SwapSrcMap(driver->GetSwapSpaceAllocator());
- } else {
- src_mapping_table_ = new SwapSrcMap(src_mapping_table->begin(), src_mapping_table->end(),
- driver->GetSwapSpaceAllocator());
- }
- mapping_table_ = mapping_table.empty() ?
- nullptr : new SwapVector<uint8_t>(mapping_table.begin(), mapping_table.end(),
- driver->GetSwapSpaceAllocator());
- vmap_table_ = new SwapVector<uint8_t>(vmap_table.begin(), vmap_table.end(),
- driver->GetSwapSpaceAllocator());
- gc_map_ = native_gc_map.empty() ? nullptr :
- new SwapVector<uint8_t>(native_gc_map.begin(), native_gc_map.end(),
- driver->GetSwapSpaceAllocator());
- cfi_info_ = cfi_info.empty() ? nullptr :
- new SwapVector<uint8_t>(cfi_info.begin(), cfi_info.end(), driver->GetSwapSpaceAllocator());
- } else {
- src_mapping_table_ = src_mapping_table == nullptr ?
- driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>()) :
- driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>(*src_mapping_table));
- mapping_table_ = mapping_table.empty() ?
- nullptr : driver->DeduplicateMappingTable(mapping_table);
- vmap_table_ = driver->DeduplicateVMapTable(vmap_table);
- gc_map_ = native_gc_map.empty() ? nullptr : driver->DeduplicateGCMap(native_gc_map);
- cfi_info_ = cfi_info.empty() ? nullptr : driver->DeduplicateCFIInfo(cfi_info);
- }
+ src_mapping_table_(
+ driver->GetCompiledMethodStorage()->DeduplicateSrcMappingTable(src_mapping_table)),
+ mapping_table_(driver->GetCompiledMethodStorage()->DeduplicateMappingTable(mapping_table)),
+ vmap_table_(driver->GetCompiledMethodStorage()->DeduplicateVMapTable(vmap_table)),
+ gc_map_(driver->GetCompiledMethodStorage()->DeduplicateGCMap(native_gc_map)),
+ cfi_info_(driver->GetCompiledMethodStorage()->DeduplicateCFIInfo(cfi_info)),
+ patches_(driver->GetCompiledMethodStorage()->DeduplicateLinkerPatches(patches)) {
}
CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
@@ -166,13 +130,13 @@ CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const SrcMapElem>& src_mapping_table,
const ArrayRef<const uint8_t>& mapping_table,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
const ArrayRef<const uint8_t>& cfi_info,
const ArrayRef<const LinkerPatch>& patches) {
- SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+ SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator());
CompiledMethod* ret = alloc.allocate(1);
alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask,
fp_spill_mask, src_mapping_table, mapping_table, vmap_table, native_gc_map,
@@ -180,22 +144,20 @@ CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
return ret;
}
-
-
void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) {
- SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator());
+ SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator());
alloc.destroy(m);
alloc.deallocate(m, 1);
}
CompiledMethod::~CompiledMethod() {
- if (owns_arrays_) {
- delete src_mapping_table_;
- delete mapping_table_;
- delete vmap_table_;
- delete gc_map_;
- delete cfi_info_;
- }
+ CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage();
+ storage->ReleaseLinkerPatches(patches_);
+ storage->ReleaseCFIInfo(cfi_info_);
+ storage->ReleaseGCMap(gc_map_);
+ storage->ReleaseVMapTable(vmap_table_);
+ storage->ReleaseMappingTable(mapping_table_);
+ storage->ReleaseSrcMappingTable(src_mapping_table_);
}
} // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index a4d2387030..15a4ba0f6f 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -23,19 +23,20 @@
#include "arch/instruction_set.h"
#include "base/bit_utils.h"
+#include "length_prefixed_array.h"
#include "method_reference.h"
#include "utils/array_ref.h"
-#include "utils/swap_space.h"
namespace art {
class CompilerDriver;
+class CompiledMethodStorage;
class CompiledCode {
public:
// For Quick to supply an code blob
CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code, bool owns_code_array);
+ const ArrayRef<const uint8_t>& quick_code);
virtual ~CompiledCode();
@@ -43,8 +44,8 @@ class CompiledCode {
return instruction_set_;
}
- const SwapVector<uint8_t>* GetQuickCode() const {
- return quick_code_;
+ ArrayRef<const uint8_t> GetQuickCode() const {
+ return GetArray(quick_code_);
}
bool operator==(const CompiledCode& rhs) const;
@@ -66,40 +67,45 @@ class CompiledCode {
static const void* CodePointer(const void* code_pointer,
InstructionSet instruction_set);
- const std::vector<uint32_t>& GetOatdataOffsetsToCompliledCodeOffset() const;
- void AddOatdataOffsetToCompliledCodeOffset(uint32_t offset);
+ protected:
+ template <typename T>
+ static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array) {
+ if (array == nullptr) {
+ return ArrayRef<const T>();
+ }
+ DCHECK_NE(array->size(), 0u);
+ return ArrayRef<const T>(&array->At(0), array->size());
+ }
+
+ CompilerDriver* GetCompilerDriver() {
+ return compiler_driver_;
+ }
private:
CompilerDriver* const compiler_driver_;
const InstructionSet instruction_set_;
- // If we own the code array (means that we free in destructor).
- const bool owns_code_array_;
-
// Used to store the PIC code for Quick.
- SwapVector<uint8_t>* quick_code_;
-
- // There are offsets from the oatdata symbol to where the offset to
- // the compiled method will be found. These are computed by the
- // OatWriter and then used by the ElfWriter to add relocations so
- // that MCLinker can update the values to the location in the linked .so.
- std::vector<uint32_t> oatdata_offsets_to_compiled_code_offset_;
+ const LengthPrefixedArray<uint8_t>* const quick_code_;
};
class SrcMapElem {
public:
uint32_t from_;
int32_t to_;
+};
- // Lexicographical compare.
- bool operator<(const SrcMapElem& other) const {
- if (from_ != other.from_) {
- return from_ < other.from_;
- }
- return to_ < other.to_;
+inline bool operator<(const SrcMapElem& lhs, const SrcMapElem& rhs) {
+ if (lhs.from_ != rhs.from_) {
+ return lhs.from_ < rhs.from_;
}
-};
+ return lhs.to_ < rhs.to_;
+}
+
+inline bool operator==(const SrcMapElem& lhs, const SrcMapElem& rhs) {
+ return lhs.from_ == rhs.from_ && lhs.to_ == rhs.to_;
+}
template <class Allocator>
class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> {
@@ -151,7 +157,6 @@ class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> {
};
using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>;
-using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>;
enum LinkerPatchType {
@@ -273,6 +278,9 @@ class LinkerPatch {
uint32_t method_idx_; // Method index for Call/Method patches.
uint32_t type_idx_; // Type index for Type patches.
uint32_t element_offset_; // Element offset in the dex cache arrays.
+ static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
+ static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
+ static_assert(sizeof(element_offset_) == sizeof(cmp1_), "needed by relational operators");
};
union {
uint32_t cmp2_; // Used for relational operators.
@@ -313,7 +321,7 @@ class CompiledMethod FINAL : public CompiledCode {
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const SrcMapElem>& src_mapping_table,
const ArrayRef<const uint8_t>& mapping_table,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
@@ -329,7 +337,7 @@ class CompiledMethod FINAL : public CompiledCode {
const size_t frame_size_in_bytes,
const uint32_t core_spill_mask,
const uint32_t fp_spill_mask,
- DefaultSrcMap* src_mapping_table,
+ const ArrayRef<const SrcMapElem>& src_mapping_table,
const ArrayRef<const uint8_t>& mapping_table,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& native_gc_map,
@@ -350,35 +358,31 @@ class CompiledMethod FINAL : public CompiledCode {
return fp_spill_mask_;
}
- const SwapSrcMap& GetSrcMappingTable() const {
- DCHECK(src_mapping_table_ != nullptr);
- return *src_mapping_table_;
+ ArrayRef<const SrcMapElem> GetSrcMappingTable() const {
+ return GetArray(src_mapping_table_);
}
- SwapVector<uint8_t> const* GetMappingTable() const {
- return mapping_table_;
+ ArrayRef<const uint8_t> GetMappingTable() const {
+ return GetArray(mapping_table_);
}
- const SwapVector<uint8_t>* GetVmapTable() const {
- DCHECK(vmap_table_ != nullptr);
- return vmap_table_;
+ ArrayRef<const uint8_t> GetVmapTable() const {
+ return GetArray(vmap_table_);
}
- SwapVector<uint8_t> const* GetGcMap() const {
- return gc_map_;
+ ArrayRef<const uint8_t> GetGcMap() const {
+ return GetArray(gc_map_);
}
- const SwapVector<uint8_t>* GetCFIInfo() const {
- return cfi_info_;
+ ArrayRef<const uint8_t> GetCFIInfo() const {
+ return GetArray(cfi_info_);
}
ArrayRef<const LinkerPatch> GetPatches() const {
- return ArrayRef<const LinkerPatch>(patches_);
+ return GetArray(patches_);
}
private:
- // Whether or not the arrays are owned by the compiled method or dedupe sets.
- const bool owns_arrays_;
// For quick code, the size of the activation used by the code.
const size_t frame_size_in_bytes_;
// For quick code, a bit mask describing spilled GPR callee-save registers.
@@ -386,19 +390,19 @@ class CompiledMethod FINAL : public CompiledCode {
// For quick code, a bit mask describing spilled FPR callee-save registers.
const uint32_t fp_spill_mask_;
// For quick code, a set of pairs (PC, DEX) mapping from native PC offset to DEX offset.
- SwapSrcMap* src_mapping_table_;
+ const LengthPrefixedArray<SrcMapElem>* const src_mapping_table_;
// For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to
// native PC offset. Size prefixed.
- SwapVector<uint8_t>* mapping_table_;
+ const LengthPrefixedArray<uint8_t>* const mapping_table_;
// For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed.
- SwapVector<uint8_t>* vmap_table_;
+ const LengthPrefixedArray<uint8_t>* const vmap_table_;
// For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers
// are live.
- SwapVector<uint8_t>* gc_map_;
+ const LengthPrefixedArray<uint8_t>* const gc_map_;
// For quick code, a FDE entry for the debug_frame section.
- SwapVector<uint8_t>* cfi_info_;
+ const LengthPrefixedArray<uint8_t>* const cfi_info_;
// For quick code, linker patches needed by the method.
- const SwapVector<LinkerPatch> patches_;
+ const LengthPrefixedArray<LinkerPatch>* const patches_;
};
} // namespace art
diff --git a/compiler/compiled_method_test.cc b/compiler/compiled_method_test.cc
new file mode 100644
index 0000000000..99ee875da2
--- /dev/null
+++ b/compiler/compiled_method_test.cc
@@ -0,0 +1,120 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "compiled_method.h"
+
+namespace art {
+
+TEST(CompiledMethod, SrcMapElemOperators) {
+ SrcMapElem elems[] = {
+ { 1u, -1 },
+ { 1u, 0 },
+ { 1u, 1 },
+ { 2u, -1 },
+ { 2u, 0 }, // Index 4.
+ { 2u, 1 },
+ { 2u, 0u }, // Index 6: Arbitrarily add identical SrcMapElem with index 4.
+ };
+
+ for (size_t i = 0; i != arraysize(elems); ++i) {
+ for (size_t j = 0; j != arraysize(elems); ++j) {
+ bool expected = (i != 6u ? i : 4u) == (j != 6u ? j : 4u);
+ EXPECT_EQ(expected, elems[i] == elems[j]) << i << " " << j;
+ }
+ }
+
+ for (size_t i = 0; i != arraysize(elems); ++i) {
+ for (size_t j = 0; j != arraysize(elems); ++j) {
+ bool expected = (i != 6u ? i : 4u) < (j != 6u ? j : 4u);
+ EXPECT_EQ(expected, elems[i] < elems[j]) << i << " " << j;
+ }
+ }
+}
+
+TEST(CompiledMethod, LinkerPatchOperators) {
+ const DexFile* dex_file1 = reinterpret_cast<const DexFile*>(1);
+ const DexFile* dex_file2 = reinterpret_cast<const DexFile*>(2);
+ LinkerPatch patches[] = {
+ LinkerPatch::MethodPatch(16u, dex_file1, 1000u),
+ LinkerPatch::MethodPatch(16u, dex_file1, 1001u),
+ LinkerPatch::MethodPatch(16u, dex_file2, 1000u),
+ LinkerPatch::MethodPatch(16u, dex_file2, 1001u), // Index 3.
+ LinkerPatch::CodePatch(16u, dex_file1, 1000u),
+ LinkerPatch::CodePatch(16u, dex_file1, 1001u),
+ LinkerPatch::CodePatch(16u, dex_file2, 1000u),
+ LinkerPatch::CodePatch(16u, dex_file2, 1001u),
+ LinkerPatch::RelativeCodePatch(16u, dex_file1, 1000u),
+ LinkerPatch::RelativeCodePatch(16u, dex_file1, 1001u),
+ LinkerPatch::RelativeCodePatch(16u, dex_file2, 1000u),
+ LinkerPatch::RelativeCodePatch(16u, dex_file2, 1001u),
+ LinkerPatch::TypePatch(16u, dex_file1, 1000u),
+ LinkerPatch::TypePatch(16u, dex_file1, 1001u),
+ LinkerPatch::TypePatch(16u, dex_file2, 1000u),
+ LinkerPatch::TypePatch(16u, dex_file2, 1001u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3000u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3001u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3000u, 2001u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3001u, 2001u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3000u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3001u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3000u, 2001u),
+ LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3001u, 2001u),
+ LinkerPatch::MethodPatch(32u, dex_file1, 1000u),
+ LinkerPatch::MethodPatch(32u, dex_file1, 1001u),
+ LinkerPatch::MethodPatch(32u, dex_file2, 1000u),
+ LinkerPatch::MethodPatch(32u, dex_file2, 1001u),
+ LinkerPatch::CodePatch(32u, dex_file1, 1000u),
+ LinkerPatch::CodePatch(32u, dex_file1, 1001u),
+ LinkerPatch::CodePatch(32u, dex_file2, 1000u),
+ LinkerPatch::CodePatch(32u, dex_file2, 1001u),
+ LinkerPatch::RelativeCodePatch(32u, dex_file1, 1000u),
+ LinkerPatch::RelativeCodePatch(32u, dex_file1, 1001u),
+ LinkerPatch::RelativeCodePatch(32u, dex_file2, 1000u),
+ LinkerPatch::RelativeCodePatch(32u, dex_file2, 1001u),
+ LinkerPatch::TypePatch(32u, dex_file1, 1000u),
+ LinkerPatch::TypePatch(32u, dex_file1, 1001u),
+ LinkerPatch::TypePatch(32u, dex_file2, 1000u),
+ LinkerPatch::TypePatch(32u, dex_file2, 1001u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3000u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3001u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3000u, 2001u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3001u, 2001u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3000u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3001u, 2000u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3000u, 2001u),
+ LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3001u, 2001u),
+ LinkerPatch::MethodPatch(16u, dex_file2, 1001u), // identical with patch as index 3.
+ };
+ constexpr size_t last_index = arraysize(patches) - 1u;
+
+ for (size_t i = 0; i != arraysize(patches); ++i) {
+ for (size_t j = 0; j != arraysize(patches); ++j) {
+ bool expected = (i != last_index ? i : 3u) == (j != last_index ? j : 3u);
+ EXPECT_EQ(expected, patches[i] == patches[j]) << i << " " << j;
+ }
+ }
+
+ for (size_t i = 0; i != arraysize(patches); ++i) {
+ for (size_t j = 0; j != arraysize(patches); ++j) {
+ bool expected = (i != last_index ? i : 3u) < (j != last_index ? j : 3u);
+ EXPECT_EQ(expected, patches[i] < patches[j]) << i << " " << j;
+ }
+ }
+}
+
+} // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index ff7ddc18c6..4836041ce0 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -356,7 +356,7 @@ CompiledMethod* ArtCompileDEX(
0,
0,
0,
- nullptr, // src_mapping_table
+ ArrayRef<const SrcMapElem>(), // src_mapping_table
ArrayRef<const uint8_t>(), // mapping_table
ArrayRef<const uint8_t>(builder.GetData()), // vmap_table
ArrayRef<const uint8_t>(), // gc_map
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index cde99b3fae..d68835a9cf 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -22,6 +22,7 @@
#endif
#include "base/bit_vector-inl.h"
+#include "base/stringprintf.h"
#include "dex/mir_graph.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
@@ -1165,7 +1166,7 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() {
cu_->compiler_driver, cu_->instruction_set,
ArrayRef<const uint8_t>(code_buffer_),
frame_size_, core_spill_mask_, fp_spill_mask_,
- &src_mapping_table_,
+ ArrayRef<const SrcMapElem>(src_mapping_table_),
ArrayRef<const uint8_t>(encoded_mapping_table_),
ArrayRef<const uint8_t>(vmap_encoder.GetData()),
ArrayRef<const uint8_t>(native_gc_map_),
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index e1a2838f3e..eaf2408763 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -756,14 +756,7 @@ uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCach
return *class_index;
}
- const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]);
- if (string_id == nullptr) {
- *class_index = kIndexNotFound;
- return *class_index;
- }
- uint32_t string_index = dex_file->GetIndexForStringId(*string_id);
-
- const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index);
+ const DexFile::TypeId* type_id = dex_file->FindTypeId(kClassCacheNames[index]);
if (type_id == nullptr) {
*class_index = kIndexNotFound;
return *class_index;
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index 18c2e55700..24daf2f15f 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -67,7 +67,6 @@ class QuickCFITest : public CFITest {
false,
false,
nullptr,
- new PassManagerOptions(),
nullptr,
false);
VerificationResults verification_results(&compiler_options);
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index d9d0434e8a..dceb118d1e 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -18,6 +18,7 @@
#include "mir_to_lir-inl.h"
+#include "base/stringprintf.h"
#include "dex/compiler_ir.h"
#include "dex/dataflow_iterator-inl.h"
#include "dex/mir_graph.h"
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index d9571c5f26..e977ebf722 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -50,7 +50,6 @@ class QuickAssembleX86TestBase : public testing::Test {
false,
false,
nullptr,
- new PassManagerOptions(),
nullptr,
false));
verification_results_.reset(new VerificationResults(compiler_options_.get()));
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc
new file mode 100644
index 0000000000..bc5c6cab87
--- /dev/null
+++ b/compiler/driver/compiled_method_storage.cc
@@ -0,0 +1,269 @@
+/*
+ * 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 <algorithm>
+#include <ostream>
+
+#include "compiled_method_storage.h"
+
+#include "base/logging.h"
+#include "compiled_method.h"
+#include "thread-inl.h"
+#include "utils.h"
+#include "utils/dedupe_set-inl.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+namespace { // anonymous namespace
+
+template <typename T>
+const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
+ DCHECK(!array.empty());
+ SwapAllocator<uint8_t> allocator(swap_space);
+ void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
+ LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
+ std::copy(array.begin(), array.end(), array_copy->begin());
+ return array_copy;
+}
+
+template <typename T>
+void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
+ SwapAllocator<uint8_t> allocator(swap_space);
+ size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
+ array->~LengthPrefixedArray<T>();
+ allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
+}
+
+} // anonymous namespace
+
+template <typename T, typename DedupeSetType>
+inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
+ const ArrayRef<const T>& data,
+ DedupeSetType* dedupe_set) {
+ if (data.empty()) {
+ return nullptr;
+ } else if (!DedupeEnabled()) {
+ return CopyArray(swap_space_.get(), data);
+ } else {
+ return dedupe_set->Add(Thread::Current(), data);
+ }
+}
+
+template <typename T>
+inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
+ const LengthPrefixedArray<T>* array) {
+ if (array != nullptr && !DedupeEnabled()) {
+ ReleaseArray(swap_space_.get(), array);
+ }
+}
+
+template <typename ContentType>
+class CompiledMethodStorage::DedupeHashFunc {
+ private:
+ static constexpr bool kUseMurmur3Hash = true;
+
+ public:
+ size_t operator()(const ArrayRef<ContentType>& array) const {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(array.data());
+ // TODO: More reasonable assertion.
+ // static_assert(IsPowerOfTwo(sizeof(ContentType)),
+ // "ContentType is not power of two, don't know whether array layout is as assumed");
+ uint32_t len = sizeof(ContentType) * array.size();
+ if (kUseMurmur3Hash) {
+ static constexpr uint32_t c1 = 0xcc9e2d51;
+ static constexpr uint32_t c2 = 0x1b873593;
+ static constexpr uint32_t r1 = 15;
+ static constexpr uint32_t r2 = 13;
+ static constexpr uint32_t m = 5;
+ static constexpr uint32_t n = 0xe6546b64;
+
+ uint32_t hash = 0;
+
+ const int nblocks = len / 4;
+ typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+ const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(data);
+ int i;
+ for (i = 0; i < nblocks; i++) {
+ uint32_t k = blocks[i];
+ k *= c1;
+ k = (k << r1) | (k >> (32 - r1));
+ k *= c2;
+
+ hash ^= k;
+ hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
+ }
+
+ const uint8_t *tail = reinterpret_cast<const uint8_t*>(data + nblocks * 4);
+ uint32_t k1 = 0;
+
+ switch (len & 3) {
+ case 3:
+ k1 ^= tail[2] << 16;
+ FALLTHROUGH_INTENDED;
+ case 2:
+ k1 ^= tail[1] << 8;
+ FALLTHROUGH_INTENDED;
+ case 1:
+ k1 ^= tail[0];
+
+ k1 *= c1;
+ k1 = (k1 << r1) | (k1 >> (32 - r1));
+ k1 *= c2;
+ hash ^= k1;
+ }
+
+ hash ^= len;
+ hash ^= (hash >> 16);
+ hash *= 0x85ebca6b;
+ hash ^= (hash >> 13);
+ hash *= 0xc2b2ae35;
+ hash ^= (hash >> 16);
+
+ return hash;
+ } else {
+ size_t hash = 0x811c9dc5;
+ for (uint32_t i = 0; i < len; ++i) {
+ hash = (hash * 16777619) ^ data[i];
+ }
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
+ return hash;
+ }
+ }
+};
+
+template <typename T>
+class CompiledMethodStorage::LengthPrefixedArrayAlloc {
+ public:
+ explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
+ : swap_space_(swap_space) {
+ }
+
+ const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
+ return CopyArray(swap_space_, array);
+ }
+
+ void Destroy(const LengthPrefixedArray<T>* array) {
+ ReleaseArray(swap_space_, array);
+ }
+
+ private:
+ SwapSpace* const swap_space_;
+};
+
+CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
+ : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+ dedupe_enabled_(true),
+ dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_src_mapping_table_("dedupe source mapping table",
+ LengthPrefixedArrayAlloc<SrcMapElem>(swap_space_.get())),
+ dedupe_mapping_table_("dedupe mapping table",
+ LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_vmap_table_("dedupe vmap table",
+ LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_gc_map_("dedupe gc map", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_linker_patches_("dedupe cfi info",
+ LengthPrefixedArrayAlloc<LinkerPatch>(swap_space_.get())) {
+}
+
+CompiledMethodStorage::~CompiledMethodStorage() {
+ // All done by member destructors.
+}
+
+void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
+ if (swap_space_.get() != nullptr) {
+ os << " swap=" << PrettySize(swap_space_->GetSize());
+ }
+ if (extended) {
+ Thread* self = Thread::Current();
+ os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
+ os << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats(self);
+ os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
+ os << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats(self);
+ os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
+ }
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
+ const ArrayRef<const uint8_t>& code) {
+ return AllocateOrDeduplicateArray(code, &dedupe_code_);
+}
+
+void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
+ ReleaseArrayIfNotDeduplicated(code);
+}
+
+const LengthPrefixedArray<SrcMapElem>* CompiledMethodStorage::DeduplicateSrcMappingTable(
+ const ArrayRef<const SrcMapElem>& src_map) {
+ return AllocateOrDeduplicateArray(src_map, &dedupe_src_mapping_table_);
+}
+
+void CompiledMethodStorage::ReleaseSrcMappingTable(const LengthPrefixedArray<SrcMapElem>* src_map) {
+ ReleaseArrayIfNotDeduplicated(src_map);
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateMappingTable(
+ const ArrayRef<const uint8_t>& table) {
+ return AllocateOrDeduplicateArray(table, &dedupe_mapping_table_);
+}
+
+void CompiledMethodStorage::ReleaseMappingTable(const LengthPrefixedArray<uint8_t>* table) {
+ ReleaseArrayIfNotDeduplicated(table);
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
+ const ArrayRef<const uint8_t>& table) {
+ return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
+}
+
+void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
+ ReleaseArrayIfNotDeduplicated(table);
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateGCMap(
+ const ArrayRef<const uint8_t>& gc_map) {
+ return AllocateOrDeduplicateArray(gc_map, &dedupe_gc_map_);
+}
+
+void CompiledMethodStorage::ReleaseGCMap(const LengthPrefixedArray<uint8_t>* gc_map) {
+ ReleaseArrayIfNotDeduplicated(gc_map);
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
+ const ArrayRef<const uint8_t>& cfi_info) {
+ return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
+}
+
+void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
+ ReleaseArrayIfNotDeduplicated(cfi_info);
+}
+
+const LengthPrefixedArray<LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
+ const ArrayRef<const LinkerPatch>& linker_patches) {
+ return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
+}
+
+void CompiledMethodStorage::ReleaseLinkerPatches(
+ const LengthPrefixedArray<LinkerPatch>* linker_patches) {
+ ReleaseArrayIfNotDeduplicated(linker_patches);
+}
+
+} // namespace art
diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h
new file mode 100644
index 0000000000..ef10b6768b
--- /dev/null
+++ b/compiler/driver/compiled_method_storage.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
+#define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
+
+#include <iosfwd>
+#include <memory>
+
+#include "base/macros.h"
+#include "length_prefixed_array.h"
+#include "utils/array_ref.h"
+#include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+class LinkerPatch;
+class SrcMapElem;
+
+class CompiledMethodStorage {
+ public:
+ explicit CompiledMethodStorage(int swap_fd);
+ ~CompiledMethodStorage();
+
+ void DumpMemoryUsage(std::ostream& os, bool extended) const;
+
+ void SetDedupeEnabled(bool dedupe_enabled) {
+ dedupe_enabled_ = dedupe_enabled;
+ }
+ bool DedupeEnabled() const {
+ return dedupe_enabled_;
+ }
+
+ SwapAllocator<void> GetSwapSpaceAllocator() {
+ return SwapAllocator<void>(swap_space_.get());
+ }
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+ void ReleaseCode(const LengthPrefixedArray<uint8_t>* code);
+
+ const LengthPrefixedArray<SrcMapElem>* DeduplicateSrcMappingTable(
+ const ArrayRef<const SrcMapElem>& src_map);
+ void ReleaseSrcMappingTable(const LengthPrefixedArray<SrcMapElem>* src_map);
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& table);
+ void ReleaseMappingTable(const LengthPrefixedArray<uint8_t>* table);
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table);
+ void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table);
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& gc_map);
+ void ReleaseGCMap(const LengthPrefixedArray<uint8_t>* gc_map);
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
+ void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info);
+
+ const LengthPrefixedArray<LinkerPatch>* DeduplicateLinkerPatches(
+ const ArrayRef<const LinkerPatch>& linker_patches);
+ void ReleaseLinkerPatches(const LengthPrefixedArray<LinkerPatch>* linker_patches);
+
+ private:
+ template <typename T, typename DedupeSetType>
+ const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data,
+ DedupeSetType* dedupe_set);
+
+ template <typename T>
+ void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array);
+
+ // DeDuplication data structures.
+ template <typename ContentType>
+ class DedupeHashFunc;
+
+ template <typename T>
+ class LengthPrefixedArrayAlloc;
+
+ template <typename T>
+ using ArrayDedupeSet = DedupeSet<ArrayRef<const T>,
+ LengthPrefixedArray<T>,
+ LengthPrefixedArrayAlloc<T>,
+ size_t,
+ DedupeHashFunc<const T>,
+ 4>;
+
+ // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+ // as other fields rely on this.
+ std::unique_ptr<SwapSpace> swap_space_;
+
+ bool dedupe_enabled_;
+
+ ArrayDedupeSet<uint8_t> dedupe_code_;
+ ArrayDedupeSet<SrcMapElem> dedupe_src_mapping_table_;
+ ArrayDedupeSet<uint8_t> dedupe_mapping_table_;
+ ArrayDedupeSet<uint8_t> dedupe_vmap_table_;
+ ArrayDedupeSet<uint8_t> dedupe_gc_map_;
+ ArrayDedupeSet<uint8_t> dedupe_cfi_info_;
+ ArrayDedupeSet<LinkerPatch> dedupe_linker_patches_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
new file mode 100644
index 0000000000..c6dbd24bf8
--- /dev/null
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -0,0 +1,160 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "compiled_method_storage.h"
+#include "compiled_method.h"
+#include "compiler_driver.h"
+#include "compiler_options.h"
+#include "dex/verification_results.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+
+namespace art {
+
+TEST(CompiledMethodStorage, Deduplicate) {
+ CompilerOptions compiler_options;
+ VerificationResults verification_results(&compiler_options);
+ DexFileToMethodInlinerMap method_inliner_map;
+ CompilerDriver driver(&compiler_options,
+ &verification_results,
+ &method_inliner_map,
+ Compiler::kOptimizing, kNone,
+ nullptr,
+ false,
+ nullptr,
+ nullptr,
+ nullptr,
+ 1u,
+ false,
+ false,
+ "",
+ false,
+ nullptr,
+ -1,
+ "");
+ CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
+
+ ASSERT_TRUE(storage->DedupeEnabled()); // The default.
+
+ const uint8_t raw_code1[] = { 1u, 2u, 3u };
+ const uint8_t raw_code2[] = { 4u, 3u, 2u, 1u };
+ ArrayRef<const uint8_t> code[] = {
+ ArrayRef<const uint8_t>(raw_code1),
+ ArrayRef<const uint8_t>(raw_code2),
+ };
+ const SrcMapElem raw_src_map1[] = { { 1u, 2u }, { 3u, 4u }, { 5u, 6u } };
+ const SrcMapElem raw_src_map2[] = { { 8u, 7u }, { 6u, 5u }, { 4u, 3u }, { 2u, 1u } };
+ ArrayRef<const SrcMapElem> src_map[] = {
+ ArrayRef<const SrcMapElem>(raw_src_map1),
+ ArrayRef<const SrcMapElem>(raw_src_map2),
+ };
+ const uint8_t raw_mapping_table1[] = { 5, 6, 7 };
+ const uint8_t raw_mapping_table2[] = { 7, 6, 5, 4 };
+ ArrayRef<const uint8_t> mapping_table[] = {
+ ArrayRef<const uint8_t>(raw_mapping_table1),
+ ArrayRef<const uint8_t>(raw_mapping_table2),
+ };
+ const uint8_t raw_vmap_table1[] = { 2, 4, 6 };
+ const uint8_t raw_vmap_table2[] = { 7, 5, 3, 1 };
+ ArrayRef<const uint8_t> vmap_table[] = {
+ ArrayRef<const uint8_t>(raw_vmap_table1),
+ ArrayRef<const uint8_t>(raw_vmap_table2),
+ };
+ const uint8_t raw_gc_map1[] = { 9, 8, 7 };
+ const uint8_t raw_gc_map2[] = { 6, 7, 8, 9 };
+ ArrayRef<const uint8_t> gc_map[] = {
+ ArrayRef<const uint8_t>(raw_gc_map1),
+ ArrayRef<const uint8_t>(raw_gc_map2),
+ };
+ const uint8_t raw_cfi_info1[] = { 1, 3, 5 };
+ const uint8_t raw_cfi_info2[] = { 8, 6, 4, 2 };
+ ArrayRef<const uint8_t> cfi_info[] = {
+ ArrayRef<const uint8_t>(raw_cfi_info1),
+ ArrayRef<const uint8_t>(raw_cfi_info2),
+ };
+ const LinkerPatch raw_patches1[] = {
+ LinkerPatch::CodePatch(0u, nullptr, 1u),
+ LinkerPatch::MethodPatch(4u, nullptr, 1u),
+ };
+ const LinkerPatch raw_patches2[] = {
+ LinkerPatch::CodePatch(0u, nullptr, 1u),
+ LinkerPatch::MethodPatch(4u, nullptr, 2u),
+ };
+ ArrayRef<const LinkerPatch> patches[] = {
+ ArrayRef<const LinkerPatch>(raw_patches1),
+ ArrayRef<const LinkerPatch>(raw_patches2),
+ };
+
+ std::vector<CompiledMethod*> compiled_methods;
+ compiled_methods.reserve(1u << 7);
+ for (auto&& c : code) {
+ for (auto&& s : src_map) {
+ for (auto&& m : mapping_table) {
+ for (auto&& v : vmap_table) {
+ for (auto&& g : gc_map) {
+ for (auto&& f : cfi_info) {
+ for (auto&& p : patches) {
+ compiled_methods.push_back(CompiledMethod::SwapAllocCompiledMethod(
+ &driver, kNone, c, 0u, 0u, 0u, s, m, v, g, f, p));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ constexpr size_t code_bit = 1u << 6;
+ constexpr size_t src_map_bit = 1u << 5;
+ constexpr size_t mapping_table_bit = 1u << 4;
+ constexpr size_t vmap_table_bit = 1u << 3;
+ constexpr size_t gc_map_bit = 1u << 2;
+ constexpr size_t cfi_info_bit = 1u << 1;
+ constexpr size_t patches_bit = 1u << 0;
+ CHECK_EQ(compiled_methods.size(), 1u << 7);
+ for (size_t i = 0; i != compiled_methods.size(); ++i) {
+ for (size_t j = 0; j != compiled_methods.size(); ++j) {
+ CompiledMethod* lhs = compiled_methods[i];
+ CompiledMethod* rhs = compiled_methods[j];
+ bool same_code = ((i ^ j) & code_bit) == 0u;
+ bool same_src_map = ((i ^ j) & src_map_bit) == 0u;
+ bool same_mapping_table = ((i ^ j) & mapping_table_bit) == 0u;
+ bool same_vmap_table = ((i ^ j) & vmap_table_bit) == 0u;
+ bool same_gc_map = ((i ^ j) & gc_map_bit) == 0u;
+ bool same_cfi_info = ((i ^ j) & cfi_info_bit) == 0u;
+ bool same_patches = ((i ^ j) & patches_bit) == 0u;
+ ASSERT_EQ(same_code, lhs->GetQuickCode().data() == rhs->GetQuickCode().data())
+ << i << " " << j;
+ ASSERT_EQ(same_src_map, lhs->GetSrcMappingTable().data() == rhs->GetSrcMappingTable().data())
+ << i << " " << j;
+ ASSERT_EQ(same_mapping_table, lhs->GetMappingTable().data() == rhs->GetMappingTable().data())
+ << i << " " << j;
+ ASSERT_EQ(same_vmap_table, lhs->GetVmapTable().data() == rhs->GetVmapTable().data())
+ << i << " " << j;
+ ASSERT_EQ(same_gc_map, lhs->GetGcMap().data() == rhs->GetGcMap().data())
+ << i << " " << j;
+ ASSERT_EQ(same_cfi_info, lhs->GetCFIInfo().data() == rhs->GetCFIInfo().data())
+ << i << " " << j;
+ ASSERT_EQ(same_patches, lhs->GetPatches().data() == rhs->GetPatches().data())
+ << i << " " << j;
+ }
+ }
+ for (CompiledMethod* method : compiled_methods) {
+ CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&driver, method);
+ }
+}
+
+} // namespace art
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 1a7dbe3a9f..14ba81d193 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -187,15 +187,11 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef
// Search dex file for localized ssb index, may fail if member's class is a parent
// of the class mentioned in the dex file and there is no dex cache entry.
std::string temp;
- const DexFile::StringId* string_id =
- dex_file->FindStringId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp));
- if (string_id != nullptr) {
- const DexFile::TypeId* type_id =
- dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id));
- if (type_id != nullptr) {
- // medium path, needs check of static storage base being initialized
- storage_idx = dex_file->GetIndexForTypeId(*type_id);
- }
+ const DexFile::TypeId* type_id =
+ dex_file->FindTypeId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp));
+ if (type_id != nullptr) {
+ // medium path, needs check of static storage base being initialized
+ storage_idx = dex_file->GetIndexForTypeId(*type_id);
}
}
if (storage_idx != DexFile::kDexNoIndex) {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index b9565846ce..d055b37ea7 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -348,9 +348,8 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
const std::string& dump_cfg_file_name, bool dump_cfg_append,
CumulativeLogger* timer, int swap_fd,
const std::string& profile_file)
- : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
- swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())),
- profile_present_(false), compiler_options_(compiler_options),
+ : profile_present_(false),
+ compiler_options_(compiler_options),
verification_results_(verification_results),
method_inliner_map_(method_inliner_map),
compiler_(Compiler::Create(this, compiler_kind)),
@@ -369,7 +368,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
had_hard_verifier_failure_(false),
thread_count_(thread_count),
stats_(new AOTCompilationStats),
- dedupe_enabled_(true),
dump_stats_(dump_stats),
dump_passes_(dump_passes),
dump_cfg_file_name_(dump_cfg_file_name),
@@ -377,12 +375,8 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
timings_logger_(timer),
compiler_context_(nullptr),
support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
- dedupe_code_("dedupe code", *swap_space_allocator_),
- dedupe_src_mapping_table_("dedupe source mapping table", *swap_space_allocator_),
- dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_),
- dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_),
- dedupe_gc_map_("dedupe gc map", *swap_space_allocator_),
- dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) {
+ dex_files_for_oat_file_(nullptr),
+ compiled_method_storage_(swap_fd) {
DCHECK(compiler_options_ != nullptr);
DCHECK(verification_results_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
@@ -402,36 +396,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options,
}
}
-SwapVector<uint8_t>* CompilerDriver::DeduplicateCode(const ArrayRef<const uint8_t>& code) {
- DCHECK(dedupe_enabled_);
- return dedupe_code_.Add(Thread::Current(), code);
-}
-
-SwapSrcMap* CompilerDriver::DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map) {
- DCHECK(dedupe_enabled_);
- return dedupe_src_mapping_table_.Add(Thread::Current(), src_map);
-}
-
-SwapVector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const ArrayRef<const uint8_t>& code) {
- DCHECK(dedupe_enabled_);
- return dedupe_mapping_table_.Add(Thread::Current(), code);
-}
-
-SwapVector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const ArrayRef<const uint8_t>& code) {
- DCHECK(dedupe_enabled_);
- return dedupe_vmap_table_.Add(Thread::Current(), code);
-}
-
-SwapVector<uint8_t>* CompilerDriver::DeduplicateGCMap(const ArrayRef<const uint8_t>& code) {
- DCHECK(dedupe_enabled_);
- return dedupe_gc_map_.Add(Thread::Current(), code);
-}
-
-SwapVector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info) {
- DCHECK(dedupe_enabled_);
- return dedupe_cfi_info_.Add(Thread::Current(), cfi_info);
-}
-
CompilerDriver::~CompilerDriver() {
Thread* self = Thread::Current();
{
@@ -447,6 +411,7 @@ CompilerDriver::~CompilerDriver() {
compiler_->UnInit();
}
+
#define CREATE_TRAMPOLINE(type, abi, offset) \
if (Is64BitInstructionSet(instruction_set_)) { \
return CreateTrampoline64(instruction_set_, abi, \
@@ -732,6 +697,9 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t
}
CompiledMethod* CompilerDriver::CompileArtMethod(Thread* self, ArtMethod* method) {
+ DCHECK_EQ(method,
+ method->GetInterfaceMethodIfProxy(
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize()));
const uint32_t method_idx = method->GetDexMethodIndex();
const uint32_t access_flags = method->GetAccessFlags();
const InvokeType invoke_type = method->GetInvokeType();
@@ -1407,8 +1375,7 @@ uint32_t CompilerDriver::GetReferenceDisableFlagOffset() const {
}
DexCacheArraysLayout CompilerDriver::GetDexCacheArraysLayout(const DexFile* dex_file) {
- // Currently only image dex caches have fixed array layout.
- return IsImage() && GetSupportBootImageFixup()
+ return ContainsElement(GetDexFilesForOatFile(), dex_file)
? DexCacheArraysLayout(GetInstructionSetPointerSize(instruction_set_), dex_file)
: DexCacheArraysLayout();
}
@@ -2642,16 +2609,7 @@ std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
oss << " native alloc=" << PrettySize(allocated_space) << " free="
<< PrettySize(free_space);
#endif
- if (swap_space_.get() != nullptr) {
- oss << " swap=" << PrettySize(swap_space_->GetSize());
- }
- if (extended) {
- oss << "\nCode dedupe: " << dedupe_code_.DumpStats();
- oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats();
- oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats();
- oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats();
- oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats();
- }
+ compiled_method_storage_.DumpMemoryUsage(oss, extended);
return oss.str();
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 0dc8261aac..4ed4dc60d2 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -30,6 +30,7 @@
#include "class_reference.h"
#include "compiler.h"
#include "dex_file.h"
+#include "driver/compiled_method_storage.h"
#include "invoke_type.h"
#include "method_reference.h"
#include "mirror/class.h" // For mirror::Class::Status.
@@ -39,9 +40,7 @@
#include "safe_map.h"
#include "thread_pool.h"
#include "utils/array_ref.h"
-#include "utils/dedupe_set.h"
#include "utils/dex_cache_arrays_layout.h"
-#include "utils/swap_space.h"
namespace art {
@@ -80,8 +79,6 @@ enum EntryPointCallingConvention {
kQuickAbi
};
-static constexpr bool kUseMurmur3Hash = true;
-
class CompilerDriver {
public:
// Create a compiler targeting the requested "instruction_set".
@@ -105,7 +102,20 @@ class CompilerDriver {
~CompilerDriver();
- void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files,
+ // Set dex files that will be stored in the oat file after being compiled.
+ void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
+ dex_files_for_oat_file_ = &dex_files;
+ }
+
+ // Get dex file that will be stored in the oat file after being compiled.
+ ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
+ return (dex_files_for_oat_file_ != nullptr)
+ ? ArrayRef<const DexFile* const>(*dex_files_for_oat_file_)
+ : ArrayRef<const DexFile* const>();
+ }
+
+ void CompileAll(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
@@ -388,10 +398,6 @@ class CompilerDriver {
support_boot_image_fixup_ = support_boot_image_fixup;
}
- SwapAllocator<void>& GetSwapSpaceAllocator() {
- return *swap_space_allocator_.get();
- }
-
bool WriteElf(const std::string& android_root,
bool is_host,
const std::vector<const DexFile*>& dex_files,
@@ -431,10 +437,10 @@ class CompilerDriver {
}
void SetDedupeEnabled(bool dedupe_enabled) {
- dedupe_enabled_ = dedupe_enabled;
+ compiled_method_storage_.SetDedupeEnabled(dedupe_enabled);
}
bool DedupeEnabled() const {
- return dedupe_enabled_;
+ return compiled_method_storage_.DedupeEnabled();
}
// Checks if class specified by type_idx is one of the image_classes_
@@ -455,13 +461,6 @@ class CompilerDriver {
uint16_t class_def_idx,
const DexFile& dex_file) const;
- SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
- SwapSrcMap* DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map);
- SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code);
- SwapVector<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& code);
- SwapVector<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& code);
- SwapVector<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
-
// Should the compiler run on this method given profile information?
bool SkipCompilation(const std::string& method_name);
@@ -479,6 +478,10 @@ class CompilerDriver {
return compiler_kind_;
}
+ CompiledMethodStorage* GetCompiledMethodStorage() {
+ return &compiled_method_storage_;
+ }
+
private:
// Return whether the declaring class of `resolved_member` is
// available to `referrer_class` for read or write access using two
@@ -599,11 +602,6 @@ class CompilerDriver {
ThreadPool* thread_pool, TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
- // as other fields rely on this.
- std::unique_ptr<SwapSpace> swap_space_;
- std::unique_ptr<SwapAllocator<void> > swap_space_allocator_;
-
ProfileFile profile_file_;
bool profile_present_;
@@ -663,7 +661,6 @@ class CompilerDriver {
class AOTCompilationStats;
std::unique_ptr<AOTCompilationStats> stats_;
- bool dedupe_enabled_;
bool dump_stats_;
const bool dump_passes_;
const std::string dump_cfg_file_name_;
@@ -678,93 +675,10 @@ class CompilerDriver {
bool support_boot_image_fixup_;
- // DeDuplication data structures, these own the corresponding byte arrays.
- template <typename ContentType>
- class DedupeHashFunc {
- public:
- size_t operator()(const ArrayRef<ContentType>& array) const {
- const uint8_t* data = reinterpret_cast<const uint8_t*>(array.data());
- static_assert(IsPowerOfTwo(sizeof(ContentType)),
- "ContentType is not power of two, don't know whether array layout is as assumed");
- uint32_t len = sizeof(ContentType) * array.size();
- if (kUseMurmur3Hash) {
- static constexpr uint32_t c1 = 0xcc9e2d51;
- static constexpr uint32_t c2 = 0x1b873593;
- static constexpr uint32_t r1 = 15;
- static constexpr uint32_t r2 = 13;
- static constexpr uint32_t m = 5;
- static constexpr uint32_t n = 0xe6546b64;
-
- uint32_t hash = 0;
-
- const int nblocks = len / 4;
- typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
- const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(data);
- int i;
- for (i = 0; i < nblocks; i++) {
- uint32_t k = blocks[i];
- k *= c1;
- k = (k << r1) | (k >> (32 - r1));
- k *= c2;
-
- hash ^= k;
- hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
- }
-
- const uint8_t *tail = reinterpret_cast<const uint8_t*>(data + nblocks * 4);
- uint32_t k1 = 0;
-
- switch (len & 3) {
- case 3:
- k1 ^= tail[2] << 16;
- FALLTHROUGH_INTENDED;
- case 2:
- k1 ^= tail[1] << 8;
- FALLTHROUGH_INTENDED;
- case 1:
- k1 ^= tail[0];
-
- k1 *= c1;
- k1 = (k1 << r1) | (k1 >> (32 - r1));
- k1 *= c2;
- hash ^= k1;
- }
-
- hash ^= len;
- hash ^= (hash >> 16);
- hash *= 0x85ebca6b;
- hash ^= (hash >> 13);
- hash *= 0xc2b2ae35;
- hash ^= (hash >> 16);
-
- return hash;
- } else {
- size_t hash = 0x811c9dc5;
- for (uint32_t i = 0; i < len; ++i) {
- hash = (hash * 16777619) ^ data[i];
- }
- hash += hash << 13;
- hash ^= hash >> 7;
- hash += hash << 3;
- hash ^= hash >> 17;
- hash += hash << 5;
- return hash;
- }
- }
- };
+ // List of dex files that will be stored in the oat file.
+ const std::vector<const DexFile*>* dex_files_for_oat_file_;
- DedupeSet<ArrayRef<const uint8_t>,
- SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_code_;
- DedupeSet<ArrayRef<SrcMapElem>,
- SwapSrcMap, size_t, DedupeHashFunc<SrcMapElem>, 4> dedupe_src_mapping_table_;
- DedupeSet<ArrayRef<const uint8_t>,
- SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_mapping_table_;
- DedupeSet<ArrayRef<const uint8_t>,
- SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_vmap_table_;
- DedupeSet<ArrayRef<const uint8_t>,
- SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_gc_map_;
- DedupeSet<ArrayRef<const uint8_t>,
- SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_cfi_info_;
+ CompiledMethodStorage compiled_method_storage_;
friend class CompileClassVisitor;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 3f5a1eabb6..a24c8a3347 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -16,6 +16,8 @@
#include "compiler_options.h"
+#include <fstream>
+
#include "dex/pass_manager.h"
namespace art {
@@ -27,8 +29,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),
+ inline_depth_limit_(kUnsetInlineDepthLimit),
+ inline_max_code_units_(kUnsetInlineMaxCodeUnits),
include_patch_information_(kDefaultIncludePatchInformation),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
@@ -38,7 +40,7 @@ CompilerOptions::CompilerOptions()
implicit_suspend_checks_(false),
compile_pic_(false),
verbose_methods_(nullptr),
- pass_manager_options_(new PassManagerOptions),
+ pass_manager_options_(),
abort_on_hard_verifier_failure_(false),
init_failure_output_(nullptr) {
}
@@ -65,7 +67,6 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
bool implicit_suspend_checks,
bool compile_pic,
const std::vector<std::string>* verbose_methods,
- PassManagerOptions* pass_manager_options,
std::ostream* init_failure_output,
bool abort_on_hard_verifier_failure
) : // NOLINT(whitespace/parens)
@@ -86,9 +87,155 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter,
implicit_suspend_checks_(implicit_suspend_checks),
compile_pic_(compile_pic),
verbose_methods_(verbose_methods),
- pass_manager_options_(pass_manager_options),
+ pass_manager_options_(),
abort_on_hard_verifier_failure_(abort_on_hard_verifier_failure),
init_failure_output_(init_failure_output) {
}
+void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
+}
+
+void CompilerOptions::ParseLargeMethodMax(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--large-method-max", &large_method_threshold_, Usage);
+}
+
+void CompilerOptions::ParseSmallMethodMax(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--small-method-max", &small_method_threshold_, Usage);
+}
+
+void CompilerOptions::ParseTinyMethodMax(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--tiny-method-max", &tiny_method_threshold_, Usage);
+}
+
+void CompilerOptions::ParseNumDexMethods(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--num-dex-methods", &num_dex_methods_threshold_, Usage);
+}
+
+void CompilerOptions::ParseInlineDepthLimit(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--inline-depth-limit", &inline_depth_limit_, Usage);
+}
+
+void CompilerOptions::ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage) {
+ ParseUintOption(option, "--inline-max-code-units=", &inline_max_code_units_, Usage);
+}
+
+void CompilerOptions::ParseDisablePasses(const StringPiece& option,
+ UsageFn Usage ATTRIBUTE_UNUSED) {
+ DCHECK(option.starts_with("--disable-passes="));
+ const std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
+ pass_manager_options_.SetDisablePassList(disable_passes);
+}
+
+void CompilerOptions::ParsePrintPasses(const StringPiece& option,
+ UsageFn Usage ATTRIBUTE_UNUSED) {
+ DCHECK(option.starts_with("--print-passes="));
+ const std::string print_passes = option.substr(strlen("--print-passes=")).data();
+ pass_manager_options_.SetPrintPassList(print_passes);
+}
+
+void CompilerOptions::ParseDumpCfgPasses(const StringPiece& option,
+ UsageFn Usage ATTRIBUTE_UNUSED) {
+ DCHECK(option.starts_with("--dump-cfg-passes="));
+ const std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
+ pass_manager_options_.SetDumpPassList(dump_passes_string);
+}
+
+void CompilerOptions::ParsePassOptions(const StringPiece& option,
+ UsageFn Usage ATTRIBUTE_UNUSED) {
+ DCHECK(option.starts_with("--pass-options="));
+ const std::string pass_options = option.substr(strlen("--pass-options=")).data();
+ pass_manager_options_.SetOverriddenPassOptions(pass_options);
+}
+
+void CompilerOptions::ParseDumpInitFailures(const StringPiece& option,
+ UsageFn Usage ATTRIBUTE_UNUSED) {
+ DCHECK(option.starts_with("--dump-init-failures="));
+ std::string file_name = option.substr(strlen("--dump-init-failures=")).data();
+ init_failure_output_.reset(new std::ofstream(file_name));
+ if (init_failure_output_.get() == nullptr) {
+ LOG(ERROR) << "Failed to allocate ofstream";
+ } else if (init_failure_output_->fail()) {
+ LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization "
+ << "failures.";
+ init_failure_output_.reset();
+ }
+}
+
+bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usage) {
+ if (option.starts_with("--compiler-filter=")) {
+ const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
+ if (strcmp(compiler_filter_string, "verify-none") == 0) {
+ compiler_filter_ = CompilerOptions::kVerifyNone;
+ } else if (strcmp(compiler_filter_string, "interpret-only") == 0) {
+ compiler_filter_ = CompilerOptions::kInterpretOnly;
+ } else if (strcmp(compiler_filter_string, "verify-at-runtime") == 0) {
+ compiler_filter_ = CompilerOptions::kVerifyAtRuntime;
+ } else if (strcmp(compiler_filter_string, "space") == 0) {
+ compiler_filter_ = CompilerOptions::kSpace;
+ } else if (strcmp(compiler_filter_string, "balanced") == 0) {
+ compiler_filter_ = CompilerOptions::kBalanced;
+ } else if (strcmp(compiler_filter_string, "speed") == 0) {
+ compiler_filter_ = CompilerOptions::kSpeed;
+ } else if (strcmp(compiler_filter_string, "everything") == 0) {
+ compiler_filter_ = CompilerOptions::kEverything;
+ } else if (strcmp(compiler_filter_string, "time") == 0) {
+ compiler_filter_ = CompilerOptions::kTime;
+ } else {
+ Usage("Unknown --compiler-filter value %s", compiler_filter_string);
+ }
+ } else if (option == "--compile-pic") {
+ compile_pic_ = true;
+ } else if (option.starts_with("--huge-method-max=")) {
+ ParseHugeMethodMax(option, Usage);
+ } else if (option.starts_with("--large-method-max=")) {
+ ParseLargeMethodMax(option, Usage);
+ } else if (option.starts_with("--small-method-max=")) {
+ ParseSmallMethodMax(option, Usage);
+ } else if (option.starts_with("--tiny-method-max=")) {
+ ParseTinyMethodMax(option, Usage);
+ } else if (option.starts_with("--num-dex-methods=")) {
+ ParseNumDexMethods(option, Usage);
+ } else if (option.starts_with("--inline-depth-limit=")) {
+ ParseInlineDepthLimit(option, Usage);
+ } else if (option.starts_with("--inline-max-code-units=")) {
+ ParseInlineMaxCodeUnits(option, Usage);
+ } else if (option == "--generate-debug-info" || option == "-g") {
+ generate_debug_info_ = true;
+ } else if (option == "--no-generate-debug-info") {
+ generate_debug_info_ = false;
+ } else if (option == "--debuggable") {
+ debuggable_ = true;
+ generate_debug_info_ = true;
+ } else if (option.starts_with("--top-k-profile-threshold=")) {
+ ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold_, Usage);
+ } else if (option == "--include-patch-information") {
+ include_patch_information_ = true;
+ } else if (option == "--no-include-patch-information") {
+ include_patch_information_ = false;
+ } else if (option == "--abort-on-hard-verifier-error") {
+ abort_on_hard_verifier_failure_ = true;
+ } else if (option == "--print-pass-names") {
+ pass_manager_options_.SetPrintPassNames(true);
+ } else if (option.starts_with("--disable-passes=")) {
+ ParseDisablePasses(option, Usage);
+ } else if (option.starts_with("--print-passes=")) {
+ ParsePrintPasses(option, Usage);
+ } else if (option == "--print-all-passes") {
+ pass_manager_options_.SetPrintAllPasses();
+ } else if (option.starts_with("--dump-cfg-passes=")) {
+ ParseDumpCfgPasses(option, Usage);
+ } else if (option == "--print-pass-options") {
+ pass_manager_options_.SetPrintPassOptions(true);
+ } else if (option.starts_with("--pass-options=")) {
+ ParsePassOptions(option, Usage);
+ } else if (option.starts_with("--dump-init-failures=")) {
+ ParseDumpInitFailures(option, Usage);
+ } else {
+ // Option not recognized.
+ return false;
+ }
+ return true;
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 18f215d165..e6acab42f2 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -22,12 +22,12 @@
#include <vector>
#include "base/macros.h"
+#include "dex/pass_manager.h"
#include "globals.h"
+#include "utils.h"
namespace art {
-class PassManagerOptions;
-
class CompilerOptions FINAL {
public:
enum CompilerFilter {
@@ -53,6 +53,8 @@ class CompilerOptions FINAL {
static const bool kDefaultIncludePatchInformation = false;
static const size_t kDefaultInlineDepthLimit = 3;
static const size_t kDefaultInlineMaxCodeUnits = 20;
+ static constexpr size_t kUnsetInlineDepthLimit = -1;
+ static constexpr size_t kUnsetInlineMaxCodeUnits = -1;
// Default inlining settings when the space filter is used.
static constexpr size_t kSpaceFilterInlineDepthLimit = 3;
@@ -78,7 +80,6 @@ class CompilerOptions FINAL {
bool implicit_suspend_checks,
bool compile_pic,
const std::vector<std::string>* verbose_methods,
- PassManagerOptions* pass_manager_options,
std::ostream* init_failure_output,
bool abort_on_hard_verifier_failure);
@@ -200,47 +201,64 @@ class CompilerOptions FINAL {
}
std::ostream* GetInitFailureOutput() const {
- return init_failure_output_;
+ return init_failure_output_.get();
}
const PassManagerOptions* GetPassManagerOptions() const {
- return pass_manager_options_.get();
+ return &pass_manager_options_;
}
bool AbortOnHardVerifierFailure() const {
return abort_on_hard_verifier_failure_;
}
+ bool ParseCompilerOption(const StringPiece& option, UsageFn Usage);
+
private:
+ void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
+ void ParsePassOptions(const StringPiece& option, UsageFn Usage);
+ void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
+ void ParsePrintPasses(const StringPiece& option, UsageFn Usage);
+ void ParseDisablePasses(const StringPiece& option, UsageFn Usage);
+ void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
+ void ParseInlineDepthLimit(const StringPiece& option, UsageFn Usage);
+ void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
+ void ParseTinyMethodMax(const StringPiece& option, UsageFn Usage);
+ void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
+ void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
+ void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
+
CompilerFilter compiler_filter_;
- const size_t huge_method_threshold_;
- const size_t large_method_threshold_;
- 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_;
+ size_t huge_method_threshold_;
+ size_t large_method_threshold_;
+ 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_;
// When using a profile file only the top K% of the profiled samples will be compiled.
- const double top_k_profile_threshold_;
- const bool debuggable_;
- const bool generate_debug_info_;
- const bool implicit_null_checks_;
- const bool implicit_so_checks_;
- const bool implicit_suspend_checks_;
- const bool compile_pic_;
+ double top_k_profile_threshold_;
+ bool debuggable_;
+ bool generate_debug_info_;
+ bool implicit_null_checks_;
+ bool implicit_so_checks_;
+ bool implicit_suspend_checks_;
+ bool compile_pic_;
// Vector of methods to have verbose output enabled for.
- const std::vector<std::string>* const verbose_methods_;
+ const std::vector<std::string>* verbose_methods_;
- std::unique_ptr<PassManagerOptions> pass_manager_options_;
+ PassManagerOptions pass_manager_options_;
// Abort compilation with an error if we find a class that fails verification with a hard
// failure.
- const bool abort_on_hard_verifier_failure_;
+ bool abort_on_hard_verifier_failure_;
// Log initialization of initialization failures to this stream if not null.
- std::ostream* const init_failure_output_;
+ std::unique_ptr<std::ostream> init_failure_output_;
+
+ friend class Dex2Oat;
DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
};
diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index a07d27c1d2..3ba380e9db 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -126,7 +126,7 @@ TEST_F(DwarfTest, DebugFrame) {
initial_opcodes, kCFIFormat, &debug_frame_data_);
std::vector<uintptr_t> debug_frame_patches;
std::vector<uintptr_t> expected_patches { 28 }; // NOLINT
- WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(),
+ WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()),
kCFIFormat, &debug_frame_data_, &debug_frame_patches);
EXPECT_EQ(expected_patches, debug_frame_patches);
@@ -142,7 +142,8 @@ TEST_F(DwarfTest, DebugFrame64) {
std::vector<uintptr_t> debug_frame_patches;
std::vector<uintptr_t> expected_patches { 32 }; // NOLINT
WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
- opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
+ ArrayRef<const uint8_t>(*opcodes.data()),
+ kCFIFormat, &debug_frame_data_, &debug_frame_patches);
DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000");
EXPECT_EQ(expected_patches, debug_frame_patches);
@@ -179,7 +180,8 @@ TEST_F(DwarfTest, x86_64_RegisterMapping) {
initial_opcodes, kCFIFormat, &debug_frame_data_);
std::vector<uintptr_t> debug_frame_patches;
WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000,
- opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches);
+ ArrayRef<const uint8_t>(*opcodes.data()),
+ kCFIFormat, &debug_frame_data_, &debug_frame_patches);
CheckObjdumpOutput(is64bit, "-W");
}
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index b7eff19bed..f3fba4b1fa 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -25,6 +25,7 @@
#include "dwarf/dwarf_constants.h"
#include "dwarf/register.h"
#include "dwarf/writer.h"
+#include "utils/array_ref.h"
namespace art {
namespace dwarf {
@@ -70,21 +71,19 @@ void WriteDebugFrameCIE(bool is64bit,
writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding.
}
}
- writer.PushData(opcodes.data());
+ writer.PushData(*opcodes.data());
writer.Pad(is64bit ? 8 : 4);
writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4);
}
// Write frame description entry (FDE) to .debug_frame or .eh_frame section.
-template<typename Vector>
+inline
void WriteDebugFrameFDE(bool is64bit, size_t cie_offset,
uint64_t initial_address, uint64_t address_range,
- const Vector* opcodes,
+ const ArrayRef<const uint8_t>& opcodes,
CFIFormat format,
std::vector<uint8_t>* debug_frame,
std::vector<uintptr_t>* debug_frame_patches) {
- static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
-
Writer<> writer(debug_frame);
size_t fde_header_start = writer.data()->size();
writer.PushUint32(0); // Length placeholder.
@@ -125,7 +124,7 @@ void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
writer.PushUint32(debug_abbrev_offset);
writer.PushUint8(entries.Is64bit() ? 8 : 4);
size_t entries_offset = writer.data()->size();
- writer.PushData(entries.data());
+ writer.PushData(*entries.data());
writer.UpdateUint32(start, writer.data()->size() - start - 4);
// Copy patch locations and make them relative to .debug_info section.
for (uintptr_t patch_location : entries.GetPatchLocations()) {
@@ -181,7 +180,7 @@ void WriteDebugLineTable(const std::vector<std::string>& include_directories,
writer.PushUint8(0); // Terminate file list.
writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4);
size_t opcodes_offset = writer.data()->size();
- writer.PushData(opcodes.data());
+ writer.PushData(*opcodes.data());
writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
// Copy patch locations and make them relative to .debug_line section.
for (uintptr_t patch_location : opcodes.GetPatchLocations()) {
diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h
index 42c32c4303..00b9dfa303 100644
--- a/compiler/dwarf/writer.h
+++ b/compiler/dwarf/writer.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_DWARF_WRITER_H_
#define ART_COMPILER_DWARF_WRITER_H_
+#include <type_traits>
#include <vector>
#include "base/bit_utils.h"
#include "base/logging.h"
@@ -119,9 +120,10 @@ class Writer {
}
template<typename Vector2>
- void PushData(const Vector2* buffer) {
- static_assert(std::is_same<typename Vector2::value_type, uint8_t>::value, "Invalid value type");
- data_->insert(data_->end(), buffer->begin(), buffer->end());
+ void PushData(const Vector2& buffer) {
+ static_assert(std::is_same<typename std::add_const<typename Vector::value_type>::type,
+ const uint8_t>::value, "Invalid value type");
+ data_->insert(data_->end(), buffer.begin(), buffer.end());
}
void UpdateUint32(size_t offset, uint32_t value) {
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index c10ffebbbc..3a9e312225 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -182,8 +182,8 @@ void WriteCFISection(const CompilerDriver* compiler,
WriteDebugFrameCIE(isa, address_type, format, debug_frame);
for (const OatWriter::DebugInfo& mi : method_infos) {
if (!mi.deduped_) { // Only one FDE per unique address.
- const SwapVector<uint8_t>* opcodes = mi.compiled_method_->GetCFIInfo();
- if (opcodes != nullptr) {
+ ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo();
+ if (!opcodes.empty()) {
address_to_fde_offset_map.emplace(mi.low_pc_, debug_frame->size());
WriteDebugFrameFDE(Is64BitInstructionSet(isa), cie_offset,
mi.low_pc_, mi.high_pc_ - mi.low_pc_,
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 7e31a7a08b..fd6cd82f7c 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -76,6 +76,7 @@ TEST_F(ImageTest, WriteRead) {
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
dex_file->EnableWrite();
}
+ compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
t.NewTiming("WriteElf");
@@ -96,8 +97,10 @@ TEST_F(ImageTest, WriteRead) {
ASSERT_TRUE(dup_oat.get() != nullptr);
{
- bool success_image =
- writer->Write(image_file.GetFilename(), dup_oat->GetPath(), dup_oat->GetPath());
+ bool success_image = writer->Write(kInvalidImageFd,
+ image_file.GetFilename(),
+ dup_oat->GetPath(),
+ dup_oat->GetPath());
ASSERT_TRUE(success_image);
bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin());
ASSERT_TRUE(success_fixup);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 4310be6464..af2a4f9426 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -122,7 +122,8 @@ bool ImageWriter::PrepareImageAddressSpace() {
return true;
}
-bool ImageWriter::Write(const std::string& image_filename,
+bool ImageWriter::Write(int image_fd,
+ const std::string& image_filename,
const std::string& oat_filename,
const std::string& oat_location) {
CHECK(!image_filename.empty());
@@ -178,10 +179,13 @@ bool ImageWriter::Write(const std::string& image_filename,
LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location;
return false;
}
-
- std::unique_ptr<File> image_file(OS::CreateEmptyFile(image_filename.c_str()));
- ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
- if (image_file.get() == nullptr) {
+ std::unique_ptr<File> image_file;
+ if (image_fd != kInvalidImageFd) {
+ image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage));
+ } else {
+ image_file.reset(OS::CreateEmptyFile(image_filename.c_str()));
+ }
+ if (image_file == nullptr) {
LOG(ERROR) << "Failed to open image file " << image_filename;
return false;
}
@@ -192,6 +196,7 @@ bool ImageWriter::Write(const std::string& image_filename,
}
// Write out the image + fields + methods.
+ ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
const auto write_count = image_header->GetImageSize();
if (!image_file->WriteFully(image_->Begin(), write_count)) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
@@ -200,7 +205,8 @@ bool ImageWriter::Write(const std::string& image_filename,
}
// Write out the image bitmap at the page aligned start of the image end.
- const ImageSection& bitmap_section = image_header->GetImageSection(ImageHeader::kSectionImageBitmap);
+ const ImageSection& bitmap_section = image_header->GetImageSection(
+ ImageHeader::kSectionImageBitmap);
CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
if (!image_file->Write(reinterpret_cast<char*>(image_bitmap_->Begin()),
bitmap_section.Size(), bitmap_section.Offset())) {
@@ -796,7 +802,7 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
offset, kNativeObjectRelocationTypeArtFieldArray });
offset += header_size;
// Forward individual fields so that we can quickly find where they belong.
- for (size_t i = 0, count = cur_fields->Length(); i < count; ++i) {
+ for (size_t i = 0, count = cur_fields->size(); i < count; ++i) {
// Need to forward arrays separate of fields.
ArtField* field = &cur_fields->At(i);
auto it2 = native_object_relocations_.find(field);
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index e235bc4553..7a2febcea1 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -41,6 +41,8 @@
namespace art {
+static constexpr int kInvalidImageFd = -1;
+
// Write a Space built during compilation for use during execution.
class ImageWriter FINAL {
public:
@@ -89,7 +91,11 @@ class ImageWriter FINAL {
uint8_t* GetOatFileBegin() const;
- bool Write(const std::string& image_filename, const std::string& oat_filename,
+ // If image_fd is not kInvalidImageFd, then we use that for the file. Otherwise we open
+ // image_filename.
+ bool Write(int image_fd,
+ const std::string& image_filename,
+ const std::string& oat_filename,
const std::string& oat_location)
REQUIRES(!Locks::mutator_lock_);
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 3d1b42f51c..d520208d32 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -63,9 +63,18 @@ extern "C" bool jit_compile_method(void* handle, ArtMethod* method, Thread* self
return jit_compiler->CompileMethod(self, method);
}
+// Callers of this method assume it has NO_RETURN.
+NO_RETURN static void Usage(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string error;
+ StringAppendV(&error, fmt, ap);
+ LOG(FATAL) << error;
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
JitCompiler::JitCompiler() : total_time_(0) {
- auto* pass_manager_options = new PassManagerOptions;
- pass_manager_options->SetDisablePassList("GVN,DCE,GVNCleanup");
compiler_options_.reset(new CompilerOptions(
CompilerOptions::kDefaultCompilerFilter,
CompilerOptions::kDefaultHugeMethodThreshold,
@@ -84,9 +93,11 @@ JitCompiler::JitCompiler() : total_time_(0) {
/* implicit_suspend_checks */ false,
/* pic */ true, // TODO: Support non-PIC in optimizing.
/* verbose_methods */ nullptr,
- pass_manager_options,
/* init_failure_output */ nullptr,
/* abort_on_hard_verifier_failure */ false));
+ for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
+ compiler_options_->ParseCompilerOption(argument, Usage);
+ }
const InstructionSet instruction_set = kRuntimeISA;
for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) {
VLOG(compiler) << "JIT compiler option " << option;
@@ -160,7 +171,7 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) {
Runtime* runtime = Runtime::Current();
// Check if the method is already compiled.
- if (runtime->GetJit()->GetCodeCache()->ContainsMethod(method)) {
+ if (runtime->GetJit()->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
VLOG(jit) << "Already compiled " << PrettyMethod(method);
return true;
}
@@ -181,7 +192,10 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) {
CompiledMethod* compiled_method = nullptr;
{
TimingLogger::ScopedTiming t2("Compiling", &logger);
- compiled_method = compiler_driver_->CompileArtMethod(self, method);
+ // If we get a request to compile a proxy method, we pass the actual Java method
+ // of that proxy method, as the compiler does not expect a proxy method.
+ ArtMethod* method_to_compile = method->GetInterfaceMethodIfProxy(sizeof(void*));
+ compiled_method = compiler_driver_->CompileArtMethod(self, method_to_compile);
}
// Trim maps to reduce memory usage.
@@ -207,10 +221,7 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method) {
result = true;
} else {
TimingLogger::ScopedTiming t2("LinkCode", &logger);
- OatFile::OatMethod oat_method(nullptr, 0);
- if (AddToCodeCache(method, compiled_method, &oat_method)) {
- oat_method.LinkMethod(method);
- CHECK(runtime->GetJit()->GetCodeCache()->ContainsMethod(method)) << PrettyMethod(method);
+ if (AddToCodeCache(method, compiled_method)) {
result = true;
}
}
@@ -227,57 +238,57 @@ CompilerCallbacks* JitCompiler::GetCompilerCallbacks() const {
}
bool JitCompiler::AddToCodeCache(ArtMethod* method,
- const CompiledMethod* compiled_method,
- OatFile::OatMethod* out_method) {
+ const CompiledMethod* compiled_method) {
Runtime* runtime = Runtime::Current();
JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache();
- const auto* quick_code = compiled_method->GetQuickCode();
- if (quick_code == nullptr) {
+ auto const quick_code = compiled_method->GetQuickCode();
+ if (quick_code.empty()) {
return false;
}
- const auto code_size = quick_code->size();
+ const auto code_size = quick_code.size();
Thread* const self = Thread::Current();
- auto* const mapping_table = compiled_method->GetMappingTable();
- auto* const vmap_table = compiled_method->GetVmapTable();
- auto* const gc_map = compiled_method->GetGcMap();
+ auto const mapping_table = compiled_method->GetMappingTable();
+ auto const vmap_table = compiled_method->GetVmapTable();
+ auto const gc_map = compiled_method->GetGcMap();
uint8_t* mapping_table_ptr = nullptr;
uint8_t* vmap_table_ptr = nullptr;
uint8_t* gc_map_ptr = nullptr;
- if (mapping_table != nullptr) {
+ if (!mapping_table.empty()) {
// Write out pre-header stuff.
mapping_table_ptr = code_cache->AddDataArray(
- self, mapping_table->data(), mapping_table->data() + mapping_table->size());
+ self, mapping_table.data(), mapping_table.data() + mapping_table.size());
if (mapping_table_ptr == nullptr) {
return false; // Out of data cache.
}
}
- if (vmap_table != nullptr) {
+ if (!vmap_table.empty()) {
vmap_table_ptr = code_cache->AddDataArray(
- self, vmap_table->data(), vmap_table->data() + vmap_table->size());
+ self, vmap_table.data(), vmap_table.data() + vmap_table.size());
if (vmap_table_ptr == nullptr) {
return false; // Out of data cache.
}
}
- if (gc_map != nullptr) {
+ if (!gc_map.empty()) {
gc_map_ptr = code_cache->AddDataArray(
- self, gc_map->data(), gc_map->data() + gc_map->size());
+ self, gc_map.data(), gc_map.data() + gc_map.size());
if (gc_map_ptr == nullptr) {
return false; // Out of data cache.
}
}
uint8_t* const code = code_cache->CommitCode(self,
+ method,
mapping_table_ptr,
vmap_table_ptr,
gc_map_ptr,
compiled_method->GetFrameSizeInBytes(),
compiled_method->GetCoreSpillMask(),
compiled_method->GetFpSpillMask(),
- compiled_method->GetQuickCode()->data(),
- compiled_method->GetQuickCode()->size());
+ compiled_method->GetQuickCode().data(),
+ compiled_method->GetQuickCode().size());
if (code == nullptr) {
return false;
@@ -285,13 +296,6 @@ bool JitCompiler::AddToCodeCache(ArtMethod* method,
const size_t thumb_offset = compiled_method->CodeDelta();
const uint32_t code_offset = sizeof(OatQuickMethodHeader) + thumb_offset;
- *out_method = OatFile::OatMethod(code, code_offset);
- DCHECK_EQ(out_method->GetGcMap(), gc_map_ptr);
- DCHECK_EQ(out_method->GetMappingTable(), mapping_table_ptr);
- DCHECK_EQ(out_method->GetVmapTable(), vmap_table_ptr);
- DCHECK_EQ(out_method->GetFrameSizeInBytes(), compiled_method->GetFrameSizeInBytes());
- DCHECK_EQ(out_method->GetCoreSpillMask(), compiled_method->GetCoreSpillMask());
- DCHECK_EQ(out_method->GetFpSpillMask(), compiled_method->GetFpSpillMask());
VLOG(jit)
<< "JIT added "
<< PrettyMethod(method) << "@" << method
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index 757f3f386a..913a6d00ae 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -59,8 +59,8 @@ class JitCompiler {
// 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_REQUIRES(Locks::mutator_lock_);
+ const CompiledMethod* compiled_method)
+ SHARED_REQUIRES(Locks::mutator_lock_);
DISALLOW_COPY_AND_ASSIGN(JitCompiler);
};
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 34f0802444..52a238233b 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -487,7 +487,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver,
frame_size,
main_jni_conv->CoreSpillMask(),
main_jni_conv->FpSpillMask(),
- nullptr, // src_mapping_table.
+ ArrayRef<const SrcMapElem>(),
ArrayRef<const uint8_t>(), // mapping_table.
ArrayRef<const uint8_t>(), // vmap_table.
ArrayRef<const uint8_t>(), // native_gc_map.
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index cb9ea38b1c..13754fdaa1 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -36,7 +36,8 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
// of code. To avoid any alignment discrepancies for the final chunk, we always align the
// offset after reserving of writing any chunk.
uint32_t aligned_offset = CompiledMethod::AlignCode(offset, instruction_set_);
- bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset, MethodReference(nullptr, 0u),
+ bool needs_thunk = ReserveSpaceProcessPatches(aligned_offset,
+ MethodReference(nullptr, 0u),
aligned_offset);
if (needs_thunk) {
thunk_locations_.push_back(aligned_offset);
@@ -85,8 +86,7 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset,
const CompiledMethod* compiled_method,
MethodReference method_ref,
uint32_t max_extra_space) {
- DCHECK(compiled_method->GetQuickCode() != nullptr);
- uint32_t quick_code_size = compiled_method->GetQuickCode()->size();
+ uint32_t quick_code_size = compiled_method->GetQuickCode().size();
uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size);
// Adjust for extra space required by the subclass.
@@ -95,7 +95,8 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset,
// We need the MethodReference for that.
if (!unprocessed_patches_.empty() &&
next_aligned_offset - unprocessed_patches_.front().second > max_positive_displacement_) {
- bool needs_thunk = ReserveSpaceProcessPatches(quick_code_offset, method_ref,
+ bool needs_thunk = ReserveSpaceProcessPatches(quick_code_offset,
+ method_ref,
next_aligned_offset);
if (needs_thunk) {
// A single thunk will cover all pending patches.
@@ -157,7 +158,10 @@ bool ArmBaseRelativePatcher::ReserveSpaceProcessPatches(uint32_t quick_code_offs
// If still unresolved, check if we have a thunk within range.
if (thunk_locations_.empty() ||
patch_offset - thunk_locations_.back() > max_negative_displacement_) {
- return next_aligned_offset - patch_offset > max_positive_displacement_;
+ // No thunk in range, we need a thunk if the next aligned offset
+ // is out of range, or if we're at the end of all code.
+ return (next_aligned_offset - patch_offset > max_positive_displacement_) ||
+ (quick_code_offset == next_aligned_offset); // End of code.
}
} else {
uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
diff --git a/compiler/linker/arm/relative_patcher_thumb2_test.cc b/compiler/linker/arm/relative_patcher_thumb2_test.cc
index 551531314a..a259cda986 100644
--- a/compiler/linker/arm/relative_patcher_thumb2_test.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2_test.cc
@@ -233,6 +233,36 @@ TEST_F(Thumb2RelativePatcherTest, CallTrampoline) {
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
+TEST_F(Thumb2RelativePatcherTest, CallTrampolineTooFar) {
+ constexpr uint32_t missing_method_index = 1024u;
+ auto method3_raw_code = GenNopsAndBl(3u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_method3 = 3u * 2u; // After NOPs.
+ ArrayRef<const uint8_t> method3_code(method3_raw_code);
+ ASSERT_EQ(bl_offset_in_method3 + 4u, method3_code.size());
+ LinkerPatch method3_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_method3, nullptr, missing_method_index),
+ };
+
+ constexpr uint32_t just_over_max_negative_disp = 16 * MB + 2 - 4u /* PC adjustment */;
+ bool thunk_in_gap = Create2MethodsWithGap(kNopCode,
+ ArrayRef<const LinkerPatch>(),
+ method3_code,
+ ArrayRef<const LinkerPatch>(method3_patches),
+ just_over_max_negative_disp - bl_offset_in_method3);
+ ASSERT_FALSE(thunk_in_gap); // There should be a thunk but it should be after the method2.
+ ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
+
+ // Check linked code.
+ uint32_t method3_offset = GetMethodOffset(3u);
+ uint32_t thunk_offset = CompiledCode::AlignCode(method3_offset + method3_code.size(), kThumb2);
+ uint32_t diff = thunk_offset - (method3_offset + bl_offset_in_method3 + 4u /* PC adjustment */);
+ ASSERT_EQ(diff & 1u, 0u);
+ ASSERT_LT(diff >> 1, 1u << 8); // Simple encoding, (diff >> 1) fits into 8 bits.
+ auto expected_code = GenNopsAndBl(3u, kBlPlus0 | ((diff >> 1) & 0xffu));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(3u), ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckThunk(thunk_offset));
+}
+
TEST_F(Thumb2RelativePatcherTest, CallOtherAlmostTooFarAfter) {
auto method1_raw_code = GenNopsAndBl(3u, kBlPlus0);
constexpr uint32_t bl_offset_in_method1 = 3u * 2u; // After NOPs.
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 6f234a8367..57018af840 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -74,7 +74,7 @@ uint32_t Arm64RelativePatcher::ReserveSpace(uint32_t offset,
// Now that we have the actual offset where the code will be placed, locate the ADRP insns
// that actually require the thunk.
uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader);
- ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode());
+ ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size());
DCHECK(compiled_method != nullptr);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc
index 857d5842b5..0bfef5e6d3 100644
--- a/compiler/linker/arm64/relative_patcher_arm64_test.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc
@@ -237,7 +237,7 @@ class Arm64RelativePatcherTest : public RelativePatcherTest {
CHECK(!compiled_method_refs_.empty());
CHECK_EQ(compiled_method_refs_[0].dex_method_index, 1u);
CHECK_EQ(compiled_method_refs_.size(), compiled_methods_.size());
- uint32_t method1_size = compiled_methods_[0]->GetQuickCode()->size();
+ uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size();
uint32_t thunk_offset = CompiledCode::AlignCode(method1_offset + method1_size, kArm64);
uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u);
ASSERT_EQ(b_diff & 3u, 0u);
@@ -386,6 +386,39 @@ TEST_F(Arm64RelativePatcherTestDefault, CallTrampoline) {
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
+TEST_F(Arm64RelativePatcherTestDefault, CallTrampolineTooFar) {
+ constexpr uint32_t missing_method_index = 1024u;
+ auto last_method_raw_code = GenNopsAndBl(1u, kBlPlus0);
+ constexpr uint32_t bl_offset_in_last_method = 1u * 4u; // After NOPs.
+ ArrayRef<const uint8_t> last_method_code(last_method_raw_code);
+ ASSERT_EQ(bl_offset_in_last_method + 4u, last_method_code.size());
+ LinkerPatch last_method_patches[] = {
+ LinkerPatch::RelativeCodePatch(bl_offset_in_last_method, nullptr, missing_method_index),
+ };
+
+ constexpr uint32_t just_over_max_negative_disp = 128 * MB + 4;
+ uint32_t last_method_idx = Create2MethodsWithGap(
+ kNopCode, ArrayRef<const LinkerPatch>(), last_method_code,
+ ArrayRef<const LinkerPatch>(last_method_patches),
+ just_over_max_negative_disp - bl_offset_in_last_method);
+ uint32_t method1_offset = GetMethodOffset(1u);
+ uint32_t last_method_offset = GetMethodOffset(last_method_idx);
+ ASSERT_EQ(method1_offset,
+ last_method_offset + bl_offset_in_last_method - just_over_max_negative_disp);
+ ASSERT_FALSE(method_offset_map_.FindMethodOffset(MethodRef(missing_method_index)).first);
+
+ // Check linked code.
+ uint32_t thunk_offset =
+ CompiledCode::AlignCode(last_method_offset + last_method_code.size(), kArm64);
+ uint32_t diff = thunk_offset - (last_method_offset + bl_offset_in_last_method);
+ ASSERT_EQ(diff & 3u, 0u);
+ ASSERT_LT(diff, 128 * MB);
+ auto expected_code = GenNopsAndBl(1u, kBlPlus0 | (diff >> 2));
+ EXPECT_TRUE(CheckLinkedMethod(MethodRef(last_method_idx),
+ ArrayRef<const uint8_t>(expected_code)));
+ EXPECT_TRUE(CheckThunk(thunk_offset));
+}
+
TEST_F(Arm64RelativePatcherTestDefault, CallOtherAlmostTooFarAfter) {
auto method1_raw_code = GenNopsAndBl(1u, kBlPlus0);
constexpr uint32_t bl_offset_in_method1 = 1u * 4u; // After NOPs.
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index e357662388..92cf8ca7ff 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -74,8 +74,8 @@ class RelativePatcherTest : public testing::Test {
compiled_method_refs_.push_back(method_ref);
compiled_methods_.emplace_back(new CompiledMethod(
&driver_, instruction_set_, code,
- 0u, 0u, 0u, nullptr, ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
- ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
+ 0u, 0u, 0u, ArrayRef<const SrcMapElem>(), ArrayRef<const uint8_t>(),
+ ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(),
patches));
}
@@ -93,7 +93,7 @@ class RelativePatcherTest : public testing::Test {
offset += sizeof(OatQuickMethodHeader);
uint32_t quick_code_offset = offset + compiled_method->CodeDelta();
- const auto& code = *compiled_method->GetQuickCode();
+ const auto code = compiled_method->GetQuickCode();
offset += code.size();
method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset);
@@ -125,7 +125,7 @@ class RelativePatcherTest : public testing::Test {
out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader));
offset += sizeof(OatQuickMethodHeader);
- ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode());
+ ArrayRef<const uint8_t> code = compiled_method->GetQuickCode();
if (!compiled_method->GetPatches().empty()) {
patched_code_.assign(code.begin(), code.end());
code = ArrayRef<const uint8_t>(patched_code_);
@@ -164,7 +164,7 @@ class RelativePatcherTest : public testing::Test {
++idx;
}
CHECK_NE(idx, compiled_method_refs_.size());
- CHECK_EQ(compiled_methods_[idx]->GetQuickCode()->size(), expected_code.size());
+ CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size());
auto result = method_offset_map_.FindMethodOffset(method_ref);
CHECK(result.first); // Must have been linked.
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 2d9d91a7ee..ea3cb667e2 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -63,9 +63,9 @@ class OatTest : public CommonCompilerTest {
EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask());
uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2);
quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned);
- const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- EXPECT_TRUE(quick_code != nullptr);
- size_t code_size = quick_code->size() * sizeof(quick_code[0]);
+ ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
+ EXPECT_FALSE(quick_code.empty());
+ size_t code_size = quick_code.size() * sizeof(quick_code[0]);
EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size))
<< PrettyMethod(method) << " " << code_size;
CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size));
@@ -98,6 +98,7 @@ TEST_F(OatTest, WriteRead) {
jobject class_loader = nullptr;
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
+ compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
}
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 640698b0e9..c7b8884214 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -33,6 +33,7 @@
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
#include "gc/space/space.h"
+#include "handle_scope-inl.h"
#include "image_writer.h"
#include "linker/relative_patcher.h"
#include "mirror/array.h"
@@ -44,7 +45,7 @@
#include "output_stream.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
-#include "handle_scope-inl.h"
+#include "type_lookup_table.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/method_verifier.h"
@@ -107,6 +108,9 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
size_oat_class_status_(0),
size_oat_class_method_bitmaps_(0),
size_oat_class_method_offsets_(0),
+ size_oat_lookup_table_alignment_(0),
+ size_oat_lookup_table_offset_(0),
+ size_oat_lookup_table_(0),
method_offset_map_() {
CHECK(key_value_store != nullptr);
@@ -129,6 +133,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
offset = InitDexFiles(offset);
}
{
+ TimingLogger::ScopedTiming split("InitLookupTables", timings);
+ offset = InitLookupTables(offset);
+ }
+ {
TimingLogger::ScopedTiming split("InitOatClasses", timings);
offset = InitOatClasses(offset);
}
@@ -172,7 +180,7 @@ OatWriter::~OatWriter() {
}
struct OatWriter::GcMapDataAccess {
- static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ static ArrayRef<const uint8_t> GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
return compiled_method->GetGcMap();
}
@@ -194,7 +202,7 @@ struct OatWriter::GcMapDataAccess {
};
struct OatWriter::MappingTableDataAccess {
- static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ static ArrayRef<const uint8_t> GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
return compiled_method->GetMappingTable();
}
@@ -216,7 +224,7 @@ struct OatWriter::MappingTableDataAccess {
};
struct OatWriter::VmapTableDataAccess {
- static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+ static ArrayRef<const uint8_t> GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
return compiled_method->GetVmapTable();
}
@@ -322,7 +330,8 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
return true;
}
- bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) {
+ bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED,
+ const ClassDataItemIterator& it) {
// Fill in the compiled_methods_ array for methods that have a
// CompiledMethod. We track the number of non-null entries in
// num_non_null_compiled_methods_ since we only want to allocate
@@ -388,8 +397,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
// Derived from CompiledMethod.
uint32_t quick_code_offset = 0;
- const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+ ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
+ uint32_t code_size = quick_code.size() * sizeof(uint8_t);
uint32_t thumb_offset = compiled_method->CodeDelta();
// Deduplicate code arrays if we are not producing debuggable code.
@@ -428,7 +437,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
uint32_t vmap_table_offset = method_header->vmap_table_offset_;
// If we don't have quick code, then we must have a vmap, as that is how the dex2dex
// compiler records its transformations.
- DCHECK(quick_code != nullptr || vmap_table_offset != 0);
+ DCHECK(!quick_code.empty() || vmap_table_offset != 0);
uint32_t gc_map_offset = method_header->gc_map_offset_;
// The code offset was 0 when the mapping/vmap table offset was set, so it's set
// to 0-offset and we need to adjust it by code_offset.
@@ -496,12 +505,12 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
} else {
status = mirror::Class::kStatusNotReady;
}
- const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap();
- if (gc_map != nullptr) {
- size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]);
+ ArrayRef<const uint8_t> gc_map = compiled_method->GetGcMap();
+ if (!gc_map.empty()) {
+ size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
bool is_native = it.MemberIsNative();
CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
- << gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+ << gc_map_size << " " << (is_native ? "true" : "false") << " "
<< (status < mirror::Class::kStatusVerified) << " " << status << " "
<< PrettyMethod(it.GetMemberIndex(), *dex_file_);
}
@@ -519,30 +528,22 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
private:
struct CodeOffsetsKeyComparator {
bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
- if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
- return lhs->GetQuickCode() < rhs->GetQuickCode();
+ // Code is deduplicated by CompilerDriver, compare only data pointers.
+ if (lhs->GetQuickCode().data() != rhs->GetQuickCode().data()) {
+ return lhs->GetQuickCode().data() < rhs->GetQuickCode().data();
}
// If the code is the same, all other fields are likely to be the same as well.
- if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) {
- return lhs->GetMappingTable() < rhs->GetMappingTable();
+ if (UNLIKELY(lhs->GetMappingTable().data() != rhs->GetMappingTable().data())) {
+ return lhs->GetMappingTable().data() < rhs->GetMappingTable().data();
}
- if (UNLIKELY(lhs->GetVmapTable() != rhs->GetVmapTable())) {
- return lhs->GetVmapTable() < rhs->GetVmapTable();
+ if (UNLIKELY(lhs->GetVmapTable().data() != rhs->GetVmapTable().data())) {
+ return lhs->GetVmapTable().data() < rhs->GetVmapTable().data();
}
- if (UNLIKELY(lhs->GetGcMap() != rhs->GetGcMap())) {
- return lhs->GetGcMap() < rhs->GetGcMap();
+ if (UNLIKELY(lhs->GetGcMap().data() != rhs->GetGcMap().data())) {
+ return lhs->GetGcMap().data() < rhs->GetGcMap().data();
}
- const auto& lhs_patches = lhs->GetPatches();
- const auto& rhs_patches = rhs->GetPatches();
- if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) {
- return lhs_patches.size() < rhs_patches.size();
- }
- auto rit = rhs_patches.begin();
- for (const LinkerPatch& lpatch : lhs_patches) {
- if (UNLIKELY(!(lpatch == *rit))) {
- return lpatch < *rit;
- }
- ++rit;
+ if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) {
+ return lhs->GetPatches().data() < rhs->GetPatches().data();
}
return false;
}
@@ -583,17 +584,17 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
- const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
- uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
+ ArrayRef<const uint8_t> map = DataAccess::GetData(compiled_method);
+ uint32_t map_size = map.size() * sizeof(map[0]);
if (map_size != 0u) {
- auto lb = dedupe_map_.lower_bound(map);
- if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map, lb->first)) {
+ auto lb = dedupe_map_.lower_bound(map.data());
+ if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map.data(), lb->first)) {
DataAccess::SetOffset(oat_class, method_offsets_index_, lb->second);
} else {
DataAccess::SetOffset(oat_class, method_offsets_index_, offset_);
- dedupe_map_.PutBefore(lb, map, offset_);
+ dedupe_map_.PutBefore(lb, map.data(), offset_);
offset_ += map_size;
- writer_->oat_header_->UpdateChecksum(&(*map)[0], map_size);
+ writer_->oat_header_->UpdateChecksum(&map[0], map_size);
}
}
++method_offsets_index_;
@@ -605,7 +606,7 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
private:
// Deduplication is already done on a pointer basis by the compiler driver,
// so we can simply compare the pointers to find out if things are duplicated.
- SafeMap<const SwapVector<uint8_t>*, uint32_t> dedupe_map_;
+ SafeMap<const uint8_t*, uint32_t> dedupe_map_;
};
class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
@@ -647,7 +648,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
UNREACHABLE();
}
- if (compiled_method != nullptr && compiled_method->GetQuickCode()->size() != 0) {
+ if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0) {
method->SetEntryPointFromQuickCompiledCodePtrSize(
reinterpret_cast<void*>(offsets.code_offset_), pointer_size_);
}
@@ -713,10 +714,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
size_t file_offset = file_offset_;
OutputStream* out = out_;
- const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode();
- // Need a wrapper if we create a copy for patching.
- ArrayRef<const uint8_t> wrapped(*quick_code);
- uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+ ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
+ uint32_t code_size = quick_code.size() * sizeof(uint8_t);
// Deduplicate code arrays.
const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_];
@@ -753,8 +752,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
DCHECK_OFFSET_();
if (!compiled_method->GetPatches().empty()) {
- patched_code_.assign(quick_code->begin(), quick_code->end());
- wrapped = ArrayRef<const uint8_t>(patched_code_);
+ patched_code_.assign(quick_code.begin(), quick_code.end());
+ quick_code = ArrayRef<const uint8_t>(patched_code_);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
if (patch.Type() == kLinkerPatchCallRelative) {
// NOTE: Relative calls across oat files are not supported.
@@ -781,8 +780,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
}
- writer_->oat_header_->UpdateChecksum(wrapped.data(), code_size);
- if (!out->WriteFully(wrapped.data(), code_size)) {
+ writer_->oat_header_->UpdateChecksum(quick_code.data(), code_size);
+ if (!out->WriteFully(quick_code.data(), code_size)) {
ReportWriteFailure("method code", it);
return false;
}
@@ -947,14 +946,14 @@ class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
++method_offsets_index_;
// Write deduplicated map.
- const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method);
- size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]);
+ ArrayRef<const uint8_t> map = DataAccess::GetData(compiled_method);
+ size_t map_size = map.size() * sizeof(map[0]);
DCHECK((map_size == 0u && map_offset == 0u) ||
(map_size != 0u && map_offset != 0u && map_offset <= offset_))
<< map_size << " " << map_offset << " " << offset_ << " "
<< PrettyMethod(it.GetMemberIndex(), *dex_file_) << " for " << DataAccess::Name();
if (map_size != 0u && map_offset == offset_) {
- if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) {
+ if (UNLIKELY(!out->WriteFully(&map[0], map_size))) {
ReportWriteFailure(it);
return false;
}
@@ -1053,11 +1052,29 @@ size_t OatWriter::InitDexFiles(size_t offset) {
oat_dex_files_[i]->dex_file_offset_ = offset;
const DexFile* dex_file = (*dex_files_)[i];
+
+ // Initialize type lookup table
+ oat_dex_files_[i]->lookup_table_ = dex_file->GetTypeLookupTable();
+
offset += dex_file->GetHeader().file_size_;
}
return offset;
}
+size_t OatWriter::InitLookupTables(size_t offset) {
+ for (OatDexFile* oat_dex_file : oat_dex_files_) {
+ if (oat_dex_file->lookup_table_ != nullptr) {
+ uint32_t aligned_offset = RoundUp(offset, 4);
+ oat_dex_file->lookup_table_offset_ = aligned_offset;
+ size_oat_lookup_table_alignment_ += aligned_offset - offset;
+ offset = aligned_offset + oat_dex_file->lookup_table_->RawDataLength();
+ } else {
+ oat_dex_file->lookup_table_offset_ = 0;
+ }
+ }
+ return offset;
+}
+
size_t OatWriter::InitOatClasses(size_t offset) {
// calculate the offsets within OatDexFiles to OatClasses
InitOatClassesMethodVisitor visitor(this, offset);
@@ -1266,6 +1283,9 @@ bool OatWriter::WriteCode(OutputStream* out) {
DO_STAT(size_oat_class_status_);
DO_STAT(size_oat_class_method_bitmaps_);
DO_STAT(size_oat_class_method_offsets_);
+ DO_STAT(size_oat_lookup_table_alignment_);
+ DO_STAT(size_oat_lookup_table_offset_);
+ DO_STAT(size_oat_lookup_table_);
#undef DO_STAT
VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
@@ -1319,6 +1339,9 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) {
}
size_dex_file_ += dex_file->GetHeader().file_size_;
}
+ if (!WriteLookupTables(out, file_offset)) {
+ return false;
+ }
for (size_t i = 0; i != oat_classes_.size(); ++i) {
if (!oat_classes_[i]->Write(this, out, file_offset)) {
PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation();
@@ -1328,6 +1351,35 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) {
return true;
}
+bool OatWriter::WriteLookupTables(OutputStream* out, const size_t file_offset) {
+ for (size_t i = 0; i < oat_dex_files_.size(); ++i) {
+ const uint32_t lookup_table_offset = oat_dex_files_[i]->lookup_table_offset_;
+ const TypeLookupTable* table = oat_dex_files_[i]->lookup_table_;
+ DCHECK_EQ(lookup_table_offset == 0, table == nullptr);
+ if (lookup_table_offset == 0) {
+ continue;
+ }
+ const uint32_t expected_offset = file_offset + lookup_table_offset;
+ off_t actual_offset = out->Seek(expected_offset, kSeekSet);
+ if (static_cast<uint32_t>(actual_offset) != expected_offset) {
+ const DexFile* dex_file = (*dex_files_)[i];
+ PLOG(ERROR) << "Failed to seek to lookup table section. Actual: " << actual_offset
+ << " Expected: " << expected_offset << " File: " << dex_file->GetLocation();
+ return false;
+ }
+ if (table != nullptr) {
+ if (!out->WriteFully(table->RawData(), table->RawDataLength())) {
+ const DexFile* dex_file = (*dex_files_)[i];
+ PLOG(ERROR) << "Failed to write lookup table for " << dex_file->GetLocation()
+ << " to " << out->GetLocation();
+ return false;
+ }
+ size_oat_lookup_table_ += table->RawDataLength();
+ }
+ }
+ return true;
+}
+
size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) {
#define VISIT(VisitorType) \
do { \
@@ -1435,6 +1487,7 @@ OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) {
dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data());
dex_file_location_checksum_ = dex_file.GetLocationChecksum();
dex_file_offset_ = 0;
+ lookup_table_offset_ = 0;
methods_offsets_.resize(dex_file.NumClassDefs());
}
@@ -1443,6 +1496,7 @@ size_t OatWriter::OatDexFile::SizeOf() const {
+ dex_file_location_size_
+ sizeof(dex_file_location_checksum_)
+ sizeof(dex_file_offset_)
+ + sizeof(lookup_table_offset_)
+ (sizeof(methods_offsets_[0]) * methods_offsets_.size());
}
@@ -1451,6 +1505,10 @@ void OatWriter::OatDexFile::UpdateChecksum(OatHeader* oat_header) const {
oat_header->UpdateChecksum(dex_file_location_data_, dex_file_location_size_);
oat_header->UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_));
oat_header->UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_));
+ oat_header->UpdateChecksum(&lookup_table_offset_, sizeof(lookup_table_offset_));
+ if (lookup_table_ != nullptr) {
+ oat_header->UpdateChecksum(lookup_table_->RawData(), lookup_table_->RawDataLength());
+ }
oat_header->UpdateChecksum(&methods_offsets_[0],
sizeof(methods_offsets_[0]) * methods_offsets_.size());
}
@@ -1479,6 +1537,11 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer,
return false;
}
oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_);
+ if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) {
+ PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_lookup_table_offset_ += sizeof(lookup_table_offset_);
if (!out->WriteFully(&methods_offsets_[0],
sizeof(methods_offsets_[0]) * methods_offsets_.size())) {
PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation();
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index d6cb65bd64..f2fe048174 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -24,8 +24,8 @@
#include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider.
#include "mem_map.h"
#include "method_reference.h"
-#include "oat.h"
#include "mirror/class.h"
+#include "oat.h"
#include "safe_map.h"
namespace art {
@@ -36,6 +36,7 @@ class CompilerDriver;
class ImageWriter;
class OutputStream;
class TimingLogger;
+class TypeLookupTable;
// OatHeader variable length with count of D OatDexFiles
//
@@ -49,6 +50,11 @@ class TimingLogger;
// ...
// Dex[D]
//
+// TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile.
+// TypeLookupTable[1]
+// ...
+// TypeLookupTable[D]
+//
// OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs
// OatClass[1] contains OatClass entries with class status, offsets to code, etc.
// ...
@@ -168,6 +174,7 @@ class OatWriter {
size_t InitOatHeader();
size_t InitOatDexFiles(size_t offset);
+ size_t InitLookupTables(size_t offset);
size_t InitDexFiles(size_t offset);
size_t InitOatClasses(size_t offset);
size_t InitOatMaps(size_t offset);
@@ -177,6 +184,7 @@ class OatWriter {
SHARED_REQUIRES(Locks::mutator_lock_);
bool WriteTables(OutputStream* out, const size_t file_offset);
+ bool WriteLookupTables(OutputStream* out, const size_t file_offset);
size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset);
size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
@@ -199,6 +207,8 @@ class OatWriter {
const uint8_t* dex_file_location_data_;
uint32_t dex_file_location_checksum_;
uint32_t dex_file_offset_;
+ uint32_t lookup_table_offset_;
+ TypeLookupTable* lookup_table_; // Owned by the dex file.
std::vector<uint32_t> methods_offsets_;
private:
@@ -333,6 +343,9 @@ class OatWriter {
uint32_t size_oat_class_status_;
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
+ uint32_t size_oat_lookup_table_alignment_;
+ uint32_t size_oat_lookup_table_offset_;
+ uint32_t size_oat_lookup_table_;
std::unique_ptr<linker::RelativePatcher> relative_patcher_;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 8ca352f573..ed193c7b61 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -774,11 +774,12 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
&string_init_offset);
// Replace calls to String.<init> with StringFactory.
if (is_string_init) {
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = ComputeDispatchInfo(is_string_init,
- string_init_offset,
- target_method,
- direct_method,
- direct_code);
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kStringInit,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ dchecked_integral_cast<uint64_t>(string_init_offset),
+ 0U
+ };
HInvoke* invoke = new (arena_) HInvokeStaticOrDirect(
arena_,
number_of_arguments - 1,
@@ -841,11 +842,12 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
clinit_check = ProcessClinitCheckForInvoke(dex_pc, method_idx, &clinit_check_requirement);
}
- HInvokeStaticOrDirect::DispatchInfo dispatch_info = ComputeDispatchInfo(is_string_init,
- string_init_offset,
- target_method,
- direct_method,
- direct_code);
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info = {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0U
+ };
invoke = new (arena_) HInvokeStaticOrDirect(arena_,
number_of_arguments,
return_type,
@@ -958,77 +960,6 @@ HClinitCheck* HGraphBuilder::ProcessClinitCheckForInvoke(
return clinit_check;
}
-HInvokeStaticOrDirect::DispatchInfo HGraphBuilder::ComputeDispatchInfo(
- bool is_string_init,
- int32_t string_init_offset,
- MethodReference target_method,
- uintptr_t direct_method,
- uintptr_t direct_code) {
- HInvokeStaticOrDirect::MethodLoadKind method_load_kind;
- HInvokeStaticOrDirect::CodePtrLocation code_ptr_location;
- uint64_t method_load_data = 0u;
- uint64_t direct_code_ptr = 0u;
-
- if (is_string_init) {
- // TODO: Use direct_method and direct_code for the appropriate StringFactory method.
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kStringInit;
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- method_load_data = string_init_offset;
- } else if (target_method.dex_file == outer_compilation_unit_->GetDexFile() &&
- target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex()) {
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
- } else {
- if (direct_method != 0u) { // Should we use a direct pointer to the method?
- if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now?
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
- method_load_data = direct_method;
- } else { // The direct pointer will be known at link time.
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup;
- }
- } else { // Use dex cache.
- DCHECK(target_method.dex_file == dex_compilation_unit_->GetDexFile());
- DexCacheArraysLayout layout =
- compiler_driver_->GetDexCacheArraysLayout(target_method.dex_file);
- if (layout.Valid()) { // Can we use PC-relative access to the dex cache arrays?
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
- method_load_data = layout.MethodOffset(target_method.dex_method_index);
- } else { // We must go through the ArtMethod's pointer to resolved methods.
- method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
- }
- }
- if (direct_code != 0u) { // Should we use a direct pointer to the code?
- if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now?
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
- direct_code_ptr = direct_code;
- } else if (compiler_driver_->IsImage() ||
- target_method.dex_file == dex_compilation_unit_->GetDexFile()) {
- // Use PC-relative calls for invokes within a multi-dex oat file.
- // TODO: Recognize when the target dex file is within the current oat file for
- // app compilation. At the moment we recognize only the boot image as multi-dex.
- // NOTE: This will require changing the ARM backend which currently falls
- // through from kCallPCRelative to kDirectCodeFixup for different dex files.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative;
- } else { // The direct pointer will be known at link time.
- // NOTE: This is used for app->boot calls when compiling an app against
- // a relocatable but not yet relocated image.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup;
- }
- } else { // We must use the code pointer from the ArtMethod.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- }
- }
-
- if (graph_->IsDebuggable()) {
- // For debuggable apps always use the code pointer from ArtMethod
- // so that we don't circumvent instrumentation stubs if installed.
- code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
- }
-
- return HInvokeStaticOrDirect::DispatchInfo {
- method_load_kind, code_ptr_location, method_load_data, direct_code_ptr };
-}
-
bool HGraphBuilder::SetupInvokeArguments(HInvoke* invoke,
uint32_t number_of_vreg_arguments,
uint32_t* args,
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 6910d5195c..9eaa4b62c5 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -276,12 +276,6 @@ class HGraphBuilder : public ValueObject {
uint32_t dex_pc,
HInvoke* invoke);
- HInvokeStaticOrDirect::DispatchInfo ComputeDispatchInfo(bool is_string_init,
- int32_t string_init_offset,
- MethodReference target_method,
- uintptr_t direct_method,
- uintptr_t direct_code);
-
bool SetupInvokeArguments(HInvoke* invoke,
uint32_t number_of_vreg_arguments,
uint32_t* args,
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 1c62dfa859..a1bb5e0838 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -379,13 +379,17 @@ void CodeGenerator::CreateCommonInvokeLocationSummary(
if (invoke->IsInvokeStaticOrDirect()) {
HInvokeStaticOrDirect* call = invoke->AsInvokeStaticOrDirect();
- if (call->IsStringInit()) {
- locations->AddTemp(visitor->GetMethodLocation());
- } else if (call->IsRecursive()) {
- locations->SetInAt(call->GetCurrentMethodInputIndex(), visitor->GetMethodLocation());
- } else {
- locations->AddTemp(visitor->GetMethodLocation());
- locations->SetInAt(call->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+ switch (call->GetMethodLoadKind()) {
+ case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
+ locations->SetInAt(call->GetCurrentMethodInputIndex(), visitor->GetMethodLocation());
+ break;
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod:
+ locations->AddTemp(visitor->GetMethodLocation());
+ locations->SetInAt(call->GetCurrentMethodInputIndex(), Location::RequiresRegister());
+ break;
+ default:
+ locations->AddTemp(visitor->GetMethodLocation());
+ break;
}
} else {
locations->AddTemp(visitor->GetMethodLocation());
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index b04dfc00b2..47b6f30450 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -172,6 +172,7 @@ class CodeGenerator {
OptimizingCompilerStats* stats = nullptr);
virtual ~CodeGenerator() {}
+ // Get the graph. This is the outermost graph, never the graph of a method being inlined.
HGraph* GetGraph() const { return graph_; }
HBasicBlock* GetNextBlockToEmit() const;
@@ -431,6 +432,12 @@ class CodeGenerator {
uint32_t dex_pc,
SlowPathCode* slow_path) = 0;
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) = 0;
+
// Generate a call to a static or direct method.
virtual void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) = 0;
// Generate a call to a virtual method.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 92a5878476..3dc3b7fba0 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -56,6 +56,8 @@ static constexpr SRegister kFpuCalleeSaves[] =
// S registers. Therefore there is no need to block it.
static constexpr DRegister DTMP = D31;
+static constexpr uint32_t kPackedSwitchJumpTableThreshold = 6;
+
#define __ down_cast<ArmAssembler*>(codegen->GetAssembler())->
#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
@@ -513,17 +515,6 @@ void CodeGeneratorARM::Finalize(CodeAllocator* allocator) {
uint32_t new_position = __ GetAdjustedPosition(old_position);
stack_map_stream_.SetStackMapNativePcOffset(i, new_position);
}
- // Adjust native pc offsets of block labels.
- for (HBasicBlock* block : *block_order_) {
- // Get the label directly from block_labels_ rather than through GetLabelOf() to avoid
- // FirstNonEmptyBlock() which could lead to adjusting a label more than once.
- DCHECK_LT(block->GetBlockId(), GetGraph()->GetBlocks().size());
- Label* block_label = &block_labels_[block->GetBlockId()];
- DCHECK_EQ(block_label->IsBound(), !block->IsSingleJump());
- if (block_label->IsBound()) {
- __ AdjustLabelPosition(block_label);
- }
- }
// Adjust pc offsets for the disassembly information.
if (disasm_info_ != nullptr) {
GeneratedCodeInterval* frame_entry_interval = disasm_info_->GetFrameEntryInterval();
@@ -538,10 +529,6 @@ void CodeGeneratorARM::Finalize(CodeAllocator* allocator) {
it.code_interval.end = __ GetAdjustedPosition(it.code_interval.end);
}
}
- // Adjust pc offsets for relative call patches.
- for (MethodPatchInfo<Label>& info : relative_call_patches_) {
- __ AdjustLabelPosition(&info.label);
- }
CodeGenerator::Finalize(allocator);
}
@@ -732,7 +719,8 @@ void CodeGeneratorARM::GenerateFrameExit() {
}
void CodeGeneratorARM::Bind(HBasicBlock* block) {
- __ Bind(GetLabelOf(block));
+ Label* label = GetLabelOf(block);
+ __ BindTrackedLabel(label);
}
Location CodeGeneratorARM::GetStackLocation(HLoadLocal* load) const {
@@ -5155,26 +5143,51 @@ void InstructionCodeGeneratorARM::HandleBitwiseOperation(HBinaryOperation* instr
}
}
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) {
+ if (desired_dispatch_info.method_load_kind ==
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative) {
+ // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0u
+ };
+ }
+ if (desired_dispatch_info.code_ptr_location ==
+ HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative) {
+ const DexFile& outer_dex_file = GetGraph()->GetDexFile();
+ if (&outer_dex_file != target_method.dex_file) {
+ // Calls across dex files are more likely to exceed the available BL range,
+ // so use absolute patch with fixup if available and kCallArtMethod otherwise.
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location =
+ (desired_dispatch_info.method_load_kind ==
+ HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup)
+ ? HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup
+ : HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+ return HInvokeStaticOrDirect::DispatchInfo {
+ desired_dispatch_info.method_load_kind,
+ code_ptr_location,
+ desired_dispatch_info.method_load_data,
+ 0u
+ };
+ }
+ }
+ return desired_dispatch_info;
+}
+
void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// For better instruction scheduling we load the direct code pointer before the method pointer.
- bool direct_code_loaded = false;
switch (invoke->GetCodePtrLocation()) {
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- if (IsSameDexFile(*invoke->GetTargetMethod().dex_file, GetGraph()->GetDexFile())) {
- break;
- }
- // Calls across dex files are more likely to exceed the available BL range,
- // so use absolute patch by falling through to kDirectCodeFixup.
- FALLTHROUGH_INTENDED;
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
// LR = code address from literal pool with link-time patch.
__ LoadLiteral(LR, DeduplicateMethodCodeLiteral(invoke->GetTargetMethod()));
- direct_code_loaded = true;
break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
// LR = invoke->GetDirectCodePtr();
__ LoadImmediate(LR, invoke->GetDirectCodePtr());
- direct_code_loaded = true;
break;
default:
break;
@@ -5197,8 +5210,10 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
DeduplicateMethodAddressLiteral(invoke->GetTargetMethod()));
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod.
- FALLTHROUGH_INTENDED;
+ // TODO: Implement this type.
+ // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
Register method_reg;
@@ -5227,20 +5242,14 @@ void CodeGeneratorARM::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
__ bl(GetFrameEntryLabel());
break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- if (!direct_code_loaded) {
- relative_call_patches_.emplace_back(invoke->GetTargetMethod());
- __ Bind(&relative_call_patches_.back().label);
- Label label;
- __ bl(&label); // Arbitrarily branch to the instruction after BL, override at link time.
- __ Bind(&label);
- break;
- }
- // If we loaded the direct code above, fall through.
- FALLTHROUGH_INTENDED;
+ relative_call_patches_.emplace_back(invoke->GetTargetMethod());
+ __ BindTrackedLabel(&relative_call_patches_.back().label);
+ // Arbitrarily branch to the BL itself, override at link time.
+ __ bl(&relative_call_patches_.back().label);
+ break;
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
// LR prepared above for better instruction scheduling.
- DCHECK(direct_code_loaded);
// LR()
__ blx(LR);
break;
@@ -5357,25 +5366,64 @@ void LocationsBuilderARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(switch_instr, LocationSummary::kNoCall);
locations->SetInAt(0, Location::RequiresRegister());
+ if (switch_instr->GetNumEntries() >= kPackedSwitchJumpTableThreshold &&
+ codegen_->GetAssembler()->IsThumb()) {
+ locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
+ if (switch_instr->GetStartValue() != 0) {
+ locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
+ }
+ }
}
void InstructionCodeGeneratorARM::VisitPackedSwitch(HPackedSwitch* switch_instr) {
int32_t lower_bound = switch_instr->GetStartValue();
- int32_t num_entries = switch_instr->GetNumEntries();
+ uint32_t num_entries = switch_instr->GetNumEntries();
LocationSummary* locations = switch_instr->GetLocations();
Register value_reg = locations->InAt(0).AsRegister<Register>();
HBasicBlock* default_block = switch_instr->GetDefaultBlock();
- // Create a series of compare/jumps.
- const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
- for (int32_t i = 0; i < num_entries; i++) {
- GenerateCompareWithImmediate(value_reg, lower_bound + i);
- __ b(codegen_->GetLabelOf(successors[i]), EQ);
- }
+ if (num_entries < kPackedSwitchJumpTableThreshold || !codegen_->GetAssembler()->IsThumb()) {
+ // Create a series of compare/jumps.
+ const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
+ for (uint32_t i = 0; i < num_entries; i++) {
+ GenerateCompareWithImmediate(value_reg, lower_bound + i);
+ __ b(codegen_->GetLabelOf(successors[i]), EQ);
+ }
+
+ // And the default for any other value.
+ if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
+ __ b(codegen_->GetLabelOf(default_block));
+ }
+ } else {
+ // Create a table lookup.
+ Register temp_reg = locations->GetTemp(0).AsRegister<Register>();
+
+ // Materialize a pointer to the switch table
+ std::vector<Label*> labels(num_entries);
+ const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
+ for (uint32_t i = 0; i < num_entries; i++) {
+ labels[i] = codegen_->GetLabelOf(successors[i]);
+ }
+ JumpTable* table = __ CreateJumpTable(std::move(labels), temp_reg);
+
+ // Remove the bias.
+ Register key_reg;
+ if (lower_bound != 0) {
+ key_reg = locations->GetTemp(1).AsRegister<Register>();
+ __ AddConstant(key_reg, value_reg, -lower_bound);
+ } else {
+ key_reg = value_reg;
+ }
+
+ // Check whether the value is in the table, jump to default block if not.
+ __ CmpConstant(key_reg, num_entries - 1);
+ __ b(codegen_->GetLabelOf(default_block), Condition::HI);
+
+ // Load the displacement from the table.
+ __ ldr(temp_reg, Address(temp_reg, key_reg, Shift::LSL, 2));
- // And the default for any other value.
- if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
- __ b(codegen_->GetLabelOf(default_block));
+ // Dispatch is a direct add to the PC (for Thumb2).
+ __ EmitJumpTableDispatch(table, temp_reg);
}
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 6900933e87..cef1095c5d 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -362,6 +362,12 @@ class CodeGeneratorARM : public CodeGenerator {
Label* GetFrameEntryLabel() { return &frame_entry_label_; }
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) OVERRIDE;
+
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 1773c06e0b..b0be446174 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2826,6 +2826,13 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codege
return false;
}
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method ATTRIBUTE_UNUSED) {
+ // On arm64 we support all dispatch types.
+ return desired_dispatch_info;
+}
+
void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// For better instruction scheduling we load the direct code pointer before the method pointer.
bool direct_code_loaded = false;
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 799f1bdcff..ab684ea538 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -388,6 +388,12 @@ class CodeGeneratorARM64 : public CodeGenerator {
return false;
}
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) OVERRIDE;
+
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8ba4556184..29d08beb97 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -19,10 +19,12 @@
#include "arch/mips/entrypoints_direct_mips.h"
#include "arch/mips/instruction_set_features_mips.h"
#include "art_method.h"
+#include "code_generator_utils.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
#include "intrinsics.h"
+#include "intrinsics_mips.h"
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
#include "offsets.h"
@@ -1000,6 +1002,9 @@ void CodeGeneratorMIPS::Move(HInstruction* instruction,
void CodeGeneratorMIPS::AddLocationAsTemp(Location location, LocationSummary* locations) {
if (location.IsRegister()) {
locations->AddTemp(location);
+ } else if (location.IsRegisterPair()) {
+ locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairLow<Register>()));
+ locations->AddTemp(Location::RegisterLocation(location.AsRegisterPairHigh<Register>()));
} else {
UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
}
@@ -2930,7 +2935,11 @@ void InstructionCodeGeneratorMIPS::VisitInvokeInterface(HInvokeInterface* invoke
}
void LocationsBuilderMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
- // TODO: intrinsic function.
+ IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
@@ -2939,18 +2948,54 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo
// invokes must have been pruned by art::PrepareForRegisterAllocation.
DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck());
- // TODO: intrinsic function.
+ IntrinsicLocationsBuilderMIPS intrinsic(codegen_);
+ if (intrinsic.TryDispatch(invoke)) {
+ return;
+ }
+
HandleInvoke(invoke);
}
-static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen ATTRIBUTE_UNUSED) {
+static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
if (invoke->GetLocations()->Intrinsified()) {
- // TODO: intrinsic function.
+ IntrinsicCodeGeneratorMIPS intrinsic(codegen);
+ intrinsic.Dispatch(invoke);
return true;
}
return false;
}
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method ATTRIBUTE_UNUSED) {
+ switch (desired_dispatch_info.method_load_kind) {
+ case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
+ // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0u
+ };
+ default:
+ break;
+ }
+ switch (desired_dispatch_info.code_ptr_location) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
+ // TODO: Implement these types. For the moment, we fall back to kCallArtMethod.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ desired_dispatch_info.method_load_kind,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ desired_dispatch_info.method_load_data,
+ 0u
+ };
+ default:
+ return desired_dispatch_info;
+ }
+}
+
void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// All registers are assumed to be correctly set up per the calling convention.
@@ -2970,13 +3015,11 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke
__ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- // TODO: Implement this type. (Needs literal support.) At the moment, the
- // CompilerDriver will not direct the backend to use this type for MIPS.
- LOG(FATAL) << "Unsupported!";
- UNREACHABLE();
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod.
- FALLTHROUGH_INTENDED;
+ // TODO: Implement these types.
+ // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
Register reg = temp.AsRegister<Register>();
@@ -3017,12 +3060,12 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke
__ Jalr(T9);
__ Nop();
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement kCallPCRelative. For the moment, we fall back to kMethodCode.
- FALLTHROUGH_INTENDED;
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // TODO: Implement kDirectCodeFixup. For the moment, we fall back to kMethodCode.
- FALLTHROUGH_INTENDED;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
+ // TODO: Implement these types.
+ // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// T9 = callee_method->entry_point_from_quick_compiled_code_;
__ LoadFromOffset(kLoadWord,
@@ -3056,7 +3099,10 @@ void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDire
}
void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
- // TODO: Try to generate intrinsics code.
+ if (TryGenerateIntrinsicCode(invoke, codegen_)) {
+ return;
+ }
+
LocationSummary* locations = invoke->GetLocations();
Location receiver = locations->InAt(0);
Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
@@ -3086,15 +3132,25 @@ void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) {
}
void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) {
- LocationSummary::CallKind call_kind = cls->CanCallRuntime() ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
- LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ Location::RegisterLocation(V0));
}
void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) {
LocationSummary* locations = cls->GetLocations();
+ if (cls->NeedsAccessCheck()) {
+ codegen_->MoveConstant(locations->GetTemp(0), cls->GetTypeIndex());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pInitializeTypeAndVerifyAccess),
+ cls,
+ cls->GetDexPc(),
+ nullptr,
+ IsDirectEntrypoint(kQuickInitializeTypeAndVerifyAccess));
+ return;
+ }
+
Register out = locations->Out().AsRegister<Register>();
Register current_method = locations->InAt(0).AsRegister<Register>();
if (cls->IsReferrersClass()) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index a571e76933..059131dcfc 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -332,6 +332,12 @@ class CodeGeneratorMIPS : public CodeGenerator {
return type == Primitive::kPrimLong;
}
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) OVERRIDE;
+
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp);
void GenerateVirtualCall(HInvokeVirtual* invoke ATTRIBUTE_UNUSED,
Location temp ATTRIBUTE_UNUSED) OVERRIDE {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 5f78285b69..55efd5f9de 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2528,6 +2528,37 @@ static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codeg
return false;
}
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method ATTRIBUTE_UNUSED) {
+ switch (desired_dispatch_info.method_load_kind) {
+ case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
+ case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
+ // TODO: Implement these types. For the moment, we fall back to kDexCacheViaMethod.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0u
+ };
+ default:
+ break;
+ }
+ switch (desired_dispatch_info.code_ptr_location) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
+ // TODO: Implement these types. For the moment, we fall back to kCallArtMethod.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ desired_dispatch_info.method_load_kind,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ desired_dispatch_info.method_load_data,
+ 0u
+ };
+ default:
+ return desired_dispatch_info;
+ }
+}
+
void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
// All registers are assumed to be correctly set up per the calling convention.
@@ -2547,13 +2578,11 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo
__ LoadConst64(temp.AsRegister<GpuRegister>(), invoke->GetMethodAddress());
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup:
- // TODO: Implement this type. (Needs literal support.) At the moment, the
- // CompilerDriver will not direct the backend to use this type for MIPS.
- LOG(FATAL) << "Unsupported!";
- UNREACHABLE();
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod.
- FALLTHROUGH_INTENDED;
+ // TODO: Implement these types.
+ // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
GpuRegister reg = temp.AsRegister<GpuRegister>();
@@ -2593,12 +2622,12 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo
// LR()
__ Jalr(T9);
break;
- case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
- // TODO: Implement kCallPCRelative. For the moment, we fall back to kMethodCode.
- FALLTHROUGH_INTENDED;
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
- // TODO: Implement kDirectCodeFixup. For the moment, we fall back to kMethodCode.
- FALLTHROUGH_INTENDED;
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative:
+ // TODO: Implement these types.
+ // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// T9 = callee_method->entry_point_from_quick_compiled_code_;
__ LoadFromOffset(kLoadDoubleword,
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index df3fc0d1e9..9bbd02759a 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -326,6 +326,12 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const { return false; }
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) OVERRIDE;
+
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke ATTRIBUTE_UNUSED,
Location temp ATTRIBUTE_UNUSED) OVERRIDE {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 963eec2529..0df7e3b30a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3757,6 +3757,34 @@ void InstructionCodeGeneratorX86::GenerateMemoryBarrier(MemBarrierKind kind) {
}
}
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method ATTRIBUTE_UNUSED) {
+ if (desired_dispatch_info.method_load_kind ==
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative) {
+ // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ 0u,
+ 0u
+ };
+ }
+ switch (desired_dispatch_info.code_ptr_location) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+ // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
+ // (Though the direct CALL ptr16:32 is available for consideration).
+ return HInvokeStaticOrDirect::DispatchInfo {
+ desired_dispatch_info.method_load_kind,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ desired_dispatch_info.method_load_data,
+ 0u
+ };
+ default:
+ return desired_dispatch_info;
+ }
+}
void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) {
Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
@@ -3777,8 +3805,10 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
__ Bind(&method_patches_.back().label); // Bind the label at the end of the "movl" insn.
break;
case HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative:
- // TODO: Implement this type. For the moment, we fall back to kDexCacheViaMethod.
- FALLTHROUGH_INTENDED;
+ // TODO: Implement this type.
+ // Currently filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod: {
Location current_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodInputIndex());
Register method_reg;
@@ -3814,9 +3844,9 @@ void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
}
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
- // (Though the direct CALL ptr16:32 is available for consideration).
- FALLTHROUGH_INTENDED;
+ // Filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// (callee_method + offset_of_quick_compiled_code)()
__ call(Address(callee_method.AsRegister<Register>(),
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index fdfc5ab69b..ac3d06c23d 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -333,6 +333,12 @@ class CodeGeneratorX86 : public CodeGenerator {
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) OVERRIDE;
+
// Generate a call to a static or direct method.
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
// Generate a call to a virtual method.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index ed2e4ca87c..5218d70995 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -473,6 +473,24 @@ inline Condition X86_64FPCondition(IfCondition cond) {
UNREACHABLE();
}
+HInvokeStaticOrDirect::DispatchInfo CodeGeneratorX86_64::GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method ATTRIBUTE_UNUSED) {
+ switch (desired_dispatch_info.code_ptr_location) {
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
+ case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
+ // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
+ return HInvokeStaticOrDirect::DispatchInfo {
+ desired_dispatch_info.method_load_kind,
+ HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod,
+ desired_dispatch_info.method_load_data,
+ 0u
+ };
+ default:
+ return desired_dispatch_info;
+ }
+}
+
void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke,
Location temp) {
// All registers are assumed to be correctly set up.
@@ -539,8 +557,9 @@ void CodeGeneratorX86_64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invo
}
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup:
case HInvokeStaticOrDirect::CodePtrLocation::kCallDirect:
- // For direct code, we actually prefer to call via the code pointer from ArtMethod*.
- FALLTHROUGH_INTENDED;
+ // Filtered out by GetSupportedInvokeStaticOrDirectDispatch().
+ LOG(FATAL) << "Unsupported";
+ UNREACHABLE();
case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
// (callee_method + offset_of_quick_compiled_code)()
__ call(Address(callee_method.AsRegister<CpuRegister>(),
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index dc86a48ce7..fc485f5bb6 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -335,6 +335,12 @@ class CodeGeneratorX86_64 : public CodeGenerator {
return false;
}
+ // Check if the desired_dispatch_info is supported. If it is, return it,
+ // otherwise return a fall-back info that should be used instead.
+ HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch(
+ const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
+ MethodReference target_method) OVERRIDE;
+
void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Location temp) OVERRIDE;
void GenerateVirtualCall(HInvokeVirtual* invoke, Location temp) OVERRIDE;
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index e0aa4ff489..57452cc076 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -27,6 +27,11 @@ class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor {
private:
void VisitShift(HBinaryOperation* shift);
+ void VisitAbove(HAbove* instruction) OVERRIDE;
+ void VisitAboveOrEqual(HAboveOrEqual* instruction) OVERRIDE;
+ void VisitBelow(HBelow* instruction) OVERRIDE;
+ void VisitBelowOrEqual(HBelowOrEqual* instruction) OVERRIDE;
+
void VisitAnd(HAnd* instruction) OVERRIDE;
void VisitCompare(HCompare* instruction) OVERRIDE;
void VisitMul(HMul* instruction) OVERRIDE;
@@ -105,6 +110,54 @@ void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instr
}
}
+void InstructionWithAbsorbingInputSimplifier::VisitAbove(HAbove* instruction) {
+ if (instruction->GetLeft()->IsConstant() &&
+ instruction->GetLeft()->AsConstant()->IsZero()) {
+ // Replace code looking like
+ // ABOVE dst, 0, src // unsigned 0 > src is always false
+ // with
+ // CONSTANT false
+ instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 0));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ }
+}
+
+void InstructionWithAbsorbingInputSimplifier::VisitAboveOrEqual(HAboveOrEqual* instruction) {
+ if (instruction->GetRight()->IsConstant() &&
+ instruction->GetRight()->AsConstant()->IsZero()) {
+ // Replace code looking like
+ // ABOVE_OR_EQUAL dst, src, 0 // unsigned src >= 0 is always true
+ // with
+ // CONSTANT true
+ instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 1));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ }
+}
+
+void InstructionWithAbsorbingInputSimplifier::VisitBelow(HBelow* instruction) {
+ if (instruction->GetRight()->IsConstant() &&
+ instruction->GetRight()->AsConstant()->IsZero()) {
+ // Replace code looking like
+ // BELOW dst, src, 0 // unsigned src < 0 is always false
+ // with
+ // CONSTANT false
+ instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 0));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ }
+}
+
+void InstructionWithAbsorbingInputSimplifier::VisitBelowOrEqual(HBelowOrEqual* instruction) {
+ if (instruction->GetLeft()->IsConstant() &&
+ instruction->GetLeft()->AsConstant()->IsZero()) {
+ // Replace code looking like
+ // BELOW_OR_EQUAL dst, 0, src // unsigned 0 <= src is always true
+ // with
+ // CONSTANT true
+ instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 1));
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ }
+}
+
void InstructionWithAbsorbingInputSimplifier::VisitAnd(HAnd* instruction) {
HConstant* input_cst = instruction->GetConstantRight();
if ((input_cst != nullptr) && input_cst->IsZero()) {
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 2feb75cc9f..e469c8d6d0 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -29,50 +29,70 @@
namespace art {
-static void TestCode(const uint16_t* data,
- const std::string& expected_before,
- const std::string& expected_after_cf,
- const std::string& expected_after_dce,
- std::function<void(HGraph*)> check_after_cf,
- Primitive::Type return_type = Primitive::kPrimInt) {
- ArenaPool pool;
- ArenaAllocator allocator(&pool);
- HGraph* graph = CreateCFG(&allocator, data, return_type);
- ASSERT_NE(graph, nullptr);
-
- graph->TryBuildingSsa();
-
- StringPrettyPrinter printer_before(graph);
- printer_before.VisitInsertionOrder();
- std::string actual_before = printer_before.str();
- ASSERT_EQ(expected_before, actual_before);
-
- std::unique_ptr<const X86InstructionSetFeatures> features_x86(
- X86InstructionSetFeatures::FromCppDefines());
- x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions());
- HConstantFolding(graph).Run();
- SSAChecker ssa_checker_cf(graph);
- ssa_checker_cf.Run();
- ASSERT_TRUE(ssa_checker_cf.IsValid());
-
- StringPrettyPrinter printer_after_cf(graph);
- printer_after_cf.VisitInsertionOrder();
- std::string actual_after_cf = printer_after_cf.str();
- ASSERT_EQ(expected_after_cf, actual_after_cf);
-
- check_after_cf(graph);
-
- HDeadCodeElimination(graph).Run();
- SSAChecker ssa_checker_dce(graph);
- ssa_checker_dce.Run();
- ASSERT_TRUE(ssa_checker_dce.IsValid());
-
- StringPrettyPrinter printer_after_dce(graph);
- printer_after_dce.VisitInsertionOrder();
- std::string actual_after_dce = printer_after_dce.str();
- ASSERT_EQ(expected_after_dce, actual_after_dce);
-}
-
+/**
+ * Fixture class for the constant folding and dce tests.
+ */
+class ConstantFoldingTest : public testing::Test {
+ public:
+ ConstantFoldingTest() : pool_(), allocator_(&pool_) {
+ graph_ = CreateGraph(&allocator_);
+ }
+
+ void TestCode(const uint16_t* data,
+ const std::string& expected_before,
+ const std::string& expected_after_cf,
+ const std::string& expected_after_dce,
+ std::function<void(HGraph*)> check_after_cf,
+ Primitive::Type return_type = Primitive::kPrimInt) {
+ graph_ = CreateCFG(&allocator_, data, return_type);
+ TestCodeOnReadyGraph(expected_before,
+ expected_after_cf,
+ expected_after_dce,
+ check_after_cf);
+ }
+
+ void TestCodeOnReadyGraph(const std::string& expected_before,
+ const std::string& expected_after_cf,
+ const std::string& expected_after_dce,
+ std::function<void(HGraph*)> check_after_cf) {
+ ASSERT_NE(graph_, nullptr);
+ graph_->TryBuildingSsa();
+
+ StringPrettyPrinter printer_before(graph_);
+ printer_before.VisitInsertionOrder();
+ std::string actual_before = printer_before.str();
+ EXPECT_EQ(expected_before, actual_before);
+
+ std::unique_ptr<const X86InstructionSetFeatures> features_x86(
+ X86InstructionSetFeatures::FromCppDefines());
+ x86::CodeGeneratorX86 codegenX86(graph_, *features_x86.get(), CompilerOptions());
+ HConstantFolding(graph_).Run();
+ SSAChecker ssa_checker_cf(graph_);
+ ssa_checker_cf.Run();
+ ASSERT_TRUE(ssa_checker_cf.IsValid());
+
+ StringPrettyPrinter printer_after_cf(graph_);
+ printer_after_cf.VisitInsertionOrder();
+ std::string actual_after_cf = printer_after_cf.str();
+ EXPECT_EQ(expected_after_cf, actual_after_cf);
+
+ check_after_cf(graph_);
+
+ HDeadCodeElimination(graph_).Run();
+ SSAChecker ssa_checker_dce(graph_);
+ ssa_checker_dce.Run();
+ ASSERT_TRUE(ssa_checker_dce.IsValid());
+
+ StringPrettyPrinter printer_after_dce(graph_);
+ printer_after_dce.VisitInsertionOrder();
+ std::string actual_after_dce = printer_after_dce.str();
+ EXPECT_EQ(expected_after_dce, actual_after_dce);
+ }
+
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+};
/**
* Tiny three-register program exercising int constant folding on negation.
@@ -84,7 +104,7 @@ static void TestCode(const uint16_t* data,
* v1 <- -v0 1. neg-int v1, v0
* return v1 2. return v1
*/
-TEST(ConstantFolding, IntConstantFoldingNegation) {
+TEST_F(ConstantFoldingTest, IntConstantFoldingNegation) {
const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 << 8 | 1 << 12,
Instruction::NEG_INT | 1 << 8 | 0 << 12,
@@ -141,7 +161,7 @@ TEST(ConstantFolding, IntConstantFoldingNegation) {
* (v2, v3) <- -(v0, v1) 1. neg-long v2, v0
* return (v2, v3) 2. return-wide v2
*/
-TEST(ConstantFolding, LongConstantFoldingNegation) {
+TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) {
const int64_t input = INT64_C(4294967296); // 2^32
const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW.
const uint16_t word1 = High16Bits(Low32Bits(input));
@@ -205,7 +225,7 @@ TEST(ConstantFolding, LongConstantFoldingNegation) {
* v2 <- v0 + v1 2. add-int v2, v0, v1
* return v2 4. return v2
*/
-TEST(ConstantFolding, IntConstantFoldingOnAddition1) {
+TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 << 8 | 1 << 12,
Instruction::CONST_4 | 1 << 8 | 2 << 12,
@@ -271,7 +291,7 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition1) {
* v2 <- v0 + v1 6. add-int v2, v0, v1
* return v2 8. return v2
*/
-TEST(ConstantFolding, IntConstantFoldingOnAddition2) {
+TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 << 8 | 1 << 12,
Instruction::CONST_4 | 1 << 8 | 2 << 12,
@@ -357,7 +377,7 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition2) {
* v2 <- v0 - v1 2. sub-int v2, v0, v1
* return v2 4. return v2
*/
-TEST(ConstantFolding, IntConstantFoldingOnSubtraction) {
+TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 << 8 | 3 << 12,
Instruction::CONST_4 | 1 << 8 | 2 << 12,
@@ -421,7 +441,7 @@ TEST(ConstantFolding, IntConstantFoldingOnSubtraction) {
* (v0, v1) + (v1, v2) 4. add-long v4, v0, v2
* return (v4, v5) 6. return-wide v4
*/
-TEST(ConstantFolding, LongConstantFoldingOnAddition) {
+TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) {
const uint16_t data[] = SIX_REGISTERS_CODE_ITEM(
Instruction::CONST_WIDE_16 | 0 << 8, 1,
Instruction::CONST_WIDE_16 | 2 << 8, 2,
@@ -486,7 +506,7 @@ TEST(ConstantFolding, LongConstantFoldingOnAddition) {
* (v0, v1) - (v1, v2) 4. sub-long v4, v0, v2
* return (v4, v5) 6. return-wide v4
*/
-TEST(ConstantFolding, LongConstantFoldingOnSubtraction) {
+TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) {
const uint16_t data[] = SIX_REGISTERS_CODE_ITEM(
Instruction::CONST_WIDE_16 | 0 << 8, 3,
Instruction::CONST_WIDE_16 | 2 << 8, 2,
@@ -560,7 +580,7 @@ TEST(ConstantFolding, LongConstantFoldingOnSubtraction) {
* L3: v2 <- v1 + 8 11. add-int/lit16 v2, v1, #+8
* return v2 13. return v2
*/
-TEST(ConstantFolding, IntConstantFoldingAndJumps) {
+TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 0 << 8 | 1 << 12,
Instruction::CONST_4 | 1 << 8 | 2 << 12,
@@ -656,7 +676,6 @@ TEST(ConstantFolding, IntConstantFoldingAndJumps) {
check_after_cf);
}
-
/**
* Three-register program with a constant (static) condition.
*
@@ -670,7 +689,7 @@ TEST(ConstantFolding, IntConstantFoldingAndJumps) {
* L1: v2 <- v0 + v1 5. add-int v2, v0, v1
* return-void 7. return
*/
-TEST(ConstantFolding, ConstantCondition) {
+TEST_F(ConstantFoldingTest, ConstantCondition) {
const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
Instruction::CONST_4 | 1 << 8 | 1 << 12,
Instruction::CONST_4 | 0 << 8 | 0 << 12,
@@ -732,4 +751,109 @@ TEST(ConstantFolding, ConstantCondition) {
check_after_cf);
}
+/**
+ * Unsigned comparisons with zero. Since these instructions are not present
+ * in the bytecode, we need to set up the graph explicitly.
+ */
+TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) {
+ graph_ = CreateGraph(&allocator_);
+ HBasicBlock* entry_block = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry_block);
+ graph_->SetEntryBlock(entry_block);
+ HBasicBlock* block = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(block);
+ HBasicBlock* exit_block = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(exit_block);
+ graph_->SetExitBlock(exit_block);
+ entry_block->AddSuccessor(block);
+ block->AddSuccessor(exit_block);
+
+ // Make various unsigned comparisons with zero against a parameter.
+ HInstruction* parameter = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), 0, 0, Primitive::kPrimInt, true);
+ entry_block->AddInstruction(parameter);
+ HInstruction* zero = graph_->GetIntConstant(0);
+ HInstruction* last;
+ block->AddInstruction(last = new (&allocator_) HAbove(zero, parameter));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HAbove(parameter, zero));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HAboveOrEqual(zero, parameter));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HAboveOrEqual(parameter, zero));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HBelow(zero, parameter));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HBelow(parameter, zero));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HBelowOrEqual(zero, parameter));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+ block->AddInstruction(last = new (&allocator_) HBelowOrEqual(parameter, zero));
+ block->AddInstruction(new (&allocator_) HDeoptimize(last, 0));
+
+ entry_block->AddInstruction(new (&allocator_) HGoto());
+ block->AddInstruction(new (&allocator_) HReturn(zero));
+ exit_block->AddInstruction(new (&allocator_) HExit());
+
+ const std::string expected_before =
+ "BasicBlock 0, succ: 1\n"
+ " 0: ParameterValue [16, 14, 12, 10, 8, 6, 4, 2]\n"
+ " 1: IntConstant [19, 16, 14, 12, 10, 8, 6, 4, 2]\n"
+ " 18: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 2: Above(1, 0) [3]\n"
+ " 3: Deoptimize(2)\n"
+ " 4: Above(0, 1) [5]\n"
+ " 5: Deoptimize(4)\n"
+ " 6: AboveOrEqual(1, 0) [7]\n"
+ " 7: Deoptimize(6)\n"
+ " 8: AboveOrEqual(0, 1) [9]\n"
+ " 9: Deoptimize(8)\n"
+ " 10: Below(1, 0) [11]\n"
+ " 11: Deoptimize(10)\n"
+ " 12: Below(0, 1) [13]\n"
+ " 13: Deoptimize(12)\n"
+ " 14: BelowOrEqual(1, 0) [15]\n"
+ " 15: Deoptimize(14)\n"
+ " 16: BelowOrEqual(0, 1) [17]\n"
+ " 17: Deoptimize(16)\n"
+ " 19: Return(1)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 20: Exit\n";
+
+ const std::string expected_after_cf =
+ "BasicBlock 0, succ: 1\n"
+ " 0: ParameterValue [16, 10, 6, 4]\n"
+ " 1: IntConstant [13, 3, 19, 16, 10, 6, 4]\n"
+ " 21: IntConstant [15, 9]\n"
+ " 18: Goto 1\n"
+ "BasicBlock 1, pred: 0, succ: 2\n"
+ " 3: Deoptimize(1)\n"
+ " 4: Above(0, 1) [5]\n"
+ " 5: Deoptimize(4)\n"
+ " 6: AboveOrEqual(1, 0) [7]\n"
+ " 7: Deoptimize(6)\n"
+ " 9: Deoptimize(21)\n"
+ " 10: Below(1, 0) [11]\n"
+ " 11: Deoptimize(10)\n"
+ " 13: Deoptimize(1)\n"
+ " 15: Deoptimize(21)\n"
+ " 16: BelowOrEqual(0, 1) [17]\n"
+ " 17: Deoptimize(16)\n"
+ " 19: Return(1)\n"
+ "BasicBlock 2, pred: 1\n"
+ " 20: Exit\n";
+
+ const std::string expected_after_dce = expected_after_cf;
+
+ auto check_after_cf = [](HGraph* graph) {
+ CHECK(graph != nullptr);
+ };
+
+ TestCodeOnReadyGraph(expected_before,
+ expected_after_cf,
+ expected_after_dce,
+ check_after_cf);
+}
+
} // namespace art
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 8968a44da8..fdf8cc9c1f 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -20,19 +20,6 @@
namespace art {
/**
- * Returns true if instruction is invariant within the given loop.
- */
-static bool IsLoopInvariant(HLoopInformation* loop, HInstruction* instruction) {
- HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation();
- if (other_loop != loop) {
- // If instruction does not occur in same loop, it is invariant
- // if it appears in an outer loop (including no loop at all).
- return other_loop == nullptr || loop->IsIn(*other_loop);
- }
- return false;
-}
-
-/**
* Since graph traversal may enter a SCC at any position, an initial representation may be rotated,
* along dependences, viz. any of (a, b, c, d), (d, a, b, c) (c, d, a, b), (b, c, d, a) assuming
* a chain of dependences (mutual independent items may occur in arbitrary order). For proper
@@ -601,15 +588,16 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
// an unsigned entity, for example, as in the following loop that uses the full range:
// for (int i = INT_MIN; i < INT_MAX; i++) // TC = UINT_MAX
// (2) The TC is only valid if the loop is taken, otherwise TC = 0, as in:
- // for (int i = 12; i < U; i++) // TC = 0 when U >= 12
+ // for (int i = 12; i < U; i++) // TC = 0 when U < 12
// If this cannot be determined at compile-time, the TC is only valid within the
- // loop-body proper, not the loop-header unless enforced with an explicit condition.
+ // loop-body proper, not the loop-header unless enforced with an explicit taken-test.
// (3) The TC is only valid if the loop is finite, otherwise TC has no value, as in:
// for (int i = 0; i <= U; i++) // TC = Inf when U = INT_MAX
// If this cannot be determined at compile-time, the TC is only valid when enforced
- // with an explicit condition.
+ // with an explicit finite-test.
// (4) For loops which early-exits, the TC forms an upper bound, as in:
// for (int i = 0; i < 10 && ....; i++) // TC <= 10
+ InductionInfo* trip_count = upper_expr;
const bool is_taken = IsTaken(lower_expr, upper_expr, cmp);
const bool is_finite = IsFinite(upper_expr, stride_value, type, cmp);
const bool cancels = (cmp == kCondLT || cmp == kCondGT) && std::abs(stride_value) == 1;
@@ -617,26 +605,36 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
// Convert exclusive integral inequality into inclusive integral inequality,
// viz. condition i < U is i <= U - 1 and condition i > U is i >= U + 1.
if (cmp == kCondLT) {
- upper_expr = CreateInvariantOp(kSub, upper_expr, CreateConstant(1, type));
+ trip_count = CreateInvariantOp(kSub, trip_count, CreateConstant(1, type));
} else if (cmp == kCondGT) {
- upper_expr = CreateInvariantOp(kAdd, upper_expr, CreateConstant(1, type));
+ trip_count = CreateInvariantOp(kAdd, trip_count, CreateConstant(1, type));
}
// Compensate for stride.
- upper_expr = CreateInvariantOp(kAdd, upper_expr, stride);
+ trip_count = CreateInvariantOp(kAdd, trip_count, stride);
}
- InductionInfo* trip_count
- = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, upper_expr, lower_expr), stride);
+ trip_count = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride);
// Assign the trip-count expression to the loop control. Clients that use the information
// should be aware that the expression is only valid under the conditions listed above.
- InductionOp tcKind = kTripCountInBodyUnsafe;
+ InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests
if (is_taken && is_finite) {
- tcKind = kTripCountInLoop;
+ tcKind = kTripCountInLoop; // needs neither test
} else if (is_finite) {
- tcKind = kTripCountInBody;
+ tcKind = kTripCountInBody; // needs taken-test
} else if (is_taken) {
- tcKind = kTripCountInLoopUnsafe;
+ tcKind = kTripCountInLoopUnsafe; // needs finite-test
}
- AssignInfo(loop, loop->GetHeader()->GetLastInstruction(), CreateTripCount(tcKind, trip_count));
+ InductionOp op = kNop;
+ switch (cmp) {
+ case kCondLT: op = kLT; break;
+ case kCondLE: op = kLE; break;
+ case kCondGT: op = kGT; break;
+ case kCondGE: op = kGE; break;
+ default: LOG(FATAL) << "CONDITION UNREACHABLE";
+ }
+ InductionInfo* taken_test = CreateInvariantOp(op, lower_expr, upper_expr);
+ AssignInfo(loop,
+ loop->GetHeader()->GetLastInstruction(),
+ CreateTripCount(tcKind, trip_count, taken_test));
}
bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr,
@@ -707,7 +705,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::LookupInfo(HLoopInf
return loop_it->second;
}
}
- if (IsLoopInvariant(loop, instruction)) {
+ if (loop->IsLoopInvariant(instruction, true)) {
InductionInfo* info = CreateInvariantFetch(instruction);
AssignInfo(loop, instruction, info);
return info;
@@ -829,12 +827,16 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
std::string inv = "(";
inv += InductionToString(info->op_a);
switch (info->operation) {
- case kNop: inv += " @ "; break;
- case kAdd: inv += " + "; break;
+ case kNop: inv += " @ "; break;
+ case kAdd: inv += " + "; break;
case kSub:
- case kNeg: inv += " - "; break;
- case kMul: inv += " * "; break;
- case kDiv: inv += " / "; break;
+ case kNeg: inv += " - "; break;
+ case kMul: inv += " * "; break;
+ case kDiv: inv += " / "; break;
+ case kLT: inv += " < "; break;
+ case kLE: inv += " <= "; break;
+ case kGT: inv += " > "; break;
+ case kGE: inv += " >= "; break;
case kFetch:
DCHECK(info->fetch);
if (IsIntAndGet(info, &value)) {
@@ -843,10 +845,10 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) {
inv += std::to_string(info->fetch->GetId()) + ":" + info->fetch->DebugName();
}
break;
- case kTripCountInLoop: inv += "TC-loop:"; break;
- case kTripCountInBody: inv += "TC-body:"; break;
- case kTripCountInLoopUnsafe: inv += "TC-loop-unsafe:"; break;
- case kTripCountInBodyUnsafe: inv += "TC-body-unsafe:"; break;
+ case kTripCountInLoop: inv += " (TC-loop) "; break;
+ case kTripCountInBody: inv += " (TC-body) "; break;
+ case kTripCountInLoopUnsafe: inv += " (TC-loop-unsafe) "; break;
+ case kTripCountInBodyUnsafe: inv += " (TC-body-unsafe) "; break;
}
inv += InductionToString(info->op_b);
return inv + ")";
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 7ab80cd676..cf354093f2 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -65,11 +65,16 @@ class HInductionVarAnalysis : public HOptimization {
kMul,
kDiv,
kFetch,
- // Trip counts (valid in full loop or only body proper; unsafe implies loop may be infinite).
- kTripCountInLoop,
- kTripCountInBody,
- kTripCountInLoopUnsafe,
- kTripCountInBodyUnsafe
+ // Trip-counts.
+ kTripCountInLoop, // valid in full loop; loop is finite
+ kTripCountInBody, // valid in body only; loop is finite
+ kTripCountInLoopUnsafe, // valid in full loop; loop may be infinite
+ kTripCountInBodyUnsafe, // valid in body only; loop may be infinite
+ // Comparisons for trip-count tests.
+ kLT,
+ kLE,
+ kGT,
+ kGE
};
/**
@@ -85,7 +90,7 @@ class HInductionVarAnalysis : public HOptimization {
* (4) periodic
* nop: a, then defined by b (repeated when exhausted)
* (5) trip-count:
- * tc: defined by b
+ * tc: defined by a, taken-test in b
*/
struct InductionInfo : public ArenaObject<kArenaAllocInductionVarAnalysis> {
InductionInfo(InductionClass ic,
@@ -119,8 +124,9 @@ class HInductionVarAnalysis : public HOptimization {
return new (graph_->GetArena()) InductionInfo(kInvariant, kFetch, nullptr, nullptr, f);
}
- InductionInfo* CreateTripCount(InductionOp op, InductionInfo* b) {
- return new (graph_->GetArena()) InductionInfo(kInvariant, op, nullptr, b, nullptr);
+ InductionInfo* CreateTripCount(InductionOp op, InductionInfo* a, InductionInfo* b) {
+ DCHECK(a != nullptr);
+ return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
}
InductionInfo* CreateInduction(InductionClass ic, InductionInfo* a, InductionInfo* b) {
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index f16da2a3f7..b7262f6b29 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -234,7 +234,7 @@ TEST_F(InductionVarAnalysisTest, FindBasicInduction) {
EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[0], 0).c_str());
// Trip-count.
- EXPECT_STREQ("(TC-loop:(100))",
+ EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str());
}
@@ -552,7 +552,7 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) {
}
EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[d], d).c_str());
// Trip-count.
- EXPECT_STREQ("(TC-loop:(100))",
+ EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))",
GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str());
}
}
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index f4842f9696..5530d261d2 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -152,7 +152,7 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
}
} else if (is_min) {
// Special case for finding minimum: minimum of trip-count in loop-body is 1.
- if (trip != nullptr && in_body && instruction == trip->op_b->fetch) {
+ if (trip != nullptr && in_body && instruction == trip->op_a->fetch) {
return Value(1);
}
}
@@ -185,14 +185,14 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct
return GetFetch(info->fetch, trip, in_body, is_min);
case HInductionVarAnalysis::kTripCountInLoop:
if (!in_body && !is_min) { // one extra!
- return GetVal(info->op_b, trip, in_body, is_min);
+ return GetVal(info->op_a, trip, in_body, is_min);
}
FALLTHROUGH_INTENDED;
case HInductionVarAnalysis::kTripCountInBody:
if (is_min) {
return Value(0);
} else if (in_body) {
- return SubValue(GetVal(info->op_b, trip, in_body, is_min), Value(1));
+ return SubValue(GetVal(info->op_a, trip, in_body, is_min), Value(1));
}
break;
default:
@@ -428,7 +428,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
return true;
case HInductionVarAnalysis::kTripCountInLoop:
if (!in_body && !is_min) { // one extra!
- return GenerateCode(info->op_b, trip, graph, block, result, in_body, is_min);
+ return GenerateCode(info->op_a, trip, graph, block, result, in_body, is_min);
}
FALLTHROUGH_INTENDED;
case HInductionVarAnalysis::kTripCountInBody:
@@ -438,7 +438,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
}
return true;
} else if (in_body) {
- if (GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+ if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) {
if (graph != nullptr) {
*result = Insert(block,
new (graph->GetArena())
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 8fbc59fb4a..ce8926ad72 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -125,7 +125,7 @@ class InductionVarRangeTest : public testing::Test {
/** Constructs a trip-count. */
HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc) {
- return iva_->CreateTripCount(HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc));
+ return iva_->CreateTripCount(HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr);
}
/** Constructs a linear a * i + b induction. */
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index e2aca3091f..353881e47a 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -32,6 +32,7 @@
#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
+#include "sharpening.h"
#include "ssa_phi_elimination.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
@@ -396,12 +397,14 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
HDeadCodeElimination dce(callee_graph, stats_);
HConstantFolding fold(callee_graph);
ReferenceTypePropagation type_propagation(callee_graph, handles_);
+ HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
InstructionSimplifier simplify(callee_graph, stats_);
IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
HOptimization* optimizations[] = {
&intrinsics,
&type_propagation,
+ &sharpening,
&simplify,
&dce,
&fold,
@@ -415,6 +418,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
size_t number_of_instructions_budget = kMaximumNumberOfHInstructions;
if (depth_ + 1 < compiler_driver_->GetCompilerOptions().GetInlineDepthLimit()) {
HInliner inliner(callee_graph,
+ codegen_,
outer_compilation_unit_,
dex_compilation_unit,
compiler_driver_,
@@ -484,12 +488,32 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method,
return false;
}
- if (!same_dex_file && current->NeedsDexCache()) {
+ if (!same_dex_file && current->NeedsDexCacheOfDeclaringClass()) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file)
<< " could not be inlined because " << current->DebugName()
<< " it is in a different dex file and requires access to the dex cache";
return false;
}
+
+ if (current->IsNewInstance() &&
+ (current->AsNewInstance()->GetEntrypoint() == kQuickAllocObjectWithAccessCheck)) {
+ // Allocation entrypoint does not handle inlined frames.
+ return false;
+ }
+
+ if (current->IsNewArray() &&
+ (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
+ // Allocation entrypoint does not handle inlined frames.
+ return false;
+ }
+
+ if (current->IsUnresolvedStaticFieldGet() ||
+ current->IsUnresolvedInstanceFieldGet() ||
+ current->IsUnresolvedStaticFieldSet() ||
+ current->IsUnresolvedInstanceFieldSet()) {
+ // Entrypoint for unresolved fields does not handle inlined frames.
+ return false;
+ }
}
}
number_of_inlined_instructions_ += number_of_instructions;
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index bce5915219..0f6a9453be 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -22,6 +22,7 @@
namespace art {
+class CodeGenerator;
class CompilerDriver;
class DexCompilationUnit;
class HGraph;
@@ -31,6 +32,7 @@ class OptimizingCompilerStats;
class HInliner : public HOptimization {
public:
HInliner(HGraph* outer_graph,
+ CodeGenerator* codegen,
const DexCompilationUnit& outer_compilation_unit,
const DexCompilationUnit& caller_compilation_unit,
CompilerDriver* compiler_driver,
@@ -40,6 +42,7 @@ class HInliner : public HOptimization {
: HOptimization(outer_graph, kInlinerPassName, stats),
outer_compilation_unit_(outer_compilation_unit),
caller_compilation_unit_(caller_compilation_unit),
+ codegen_(codegen),
compiler_driver_(compiler_driver),
depth_(depth),
number_of_inlined_instructions_(0),
@@ -57,6 +60,7 @@ class HInliner : public HOptimization {
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
+ CodeGenerator* const codegen_;
CompilerDriver* const compiler_driver_;
const size_t depth_;
size_t number_of_inlined_instructions_;
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
new file mode 100644
index 0000000000..5efcf4eadf
--- /dev/null
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -0,0 +1,230 @@
+/*
+ * 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 "intrinsics_mips.h"
+
+#include "arch/mips/instruction_set_features_mips.h"
+#include "art_method.h"
+#include "code_generator_mips.h"
+#include "entrypoints/quick/quick_entrypoints.h"
+#include "intrinsics.h"
+#include "mirror/array-inl.h"
+#include "mirror/string.h"
+#include "thread.h"
+#include "utils/mips/assembler_mips.h"
+#include "utils/mips/constants_mips.h"
+
+namespace art {
+
+namespace mips {
+
+IntrinsicLocationsBuilderMIPS::IntrinsicLocationsBuilderMIPS(CodeGeneratorMIPS* codegen)
+ : arena_(codegen->GetGraph()->GetArena()) {
+}
+
+MipsAssembler* IntrinsicCodeGeneratorMIPS::GetAssembler() {
+ return reinterpret_cast<MipsAssembler*>(codegen_->GetAssembler());
+}
+
+ArenaAllocator* IntrinsicCodeGeneratorMIPS::GetAllocator() {
+ return codegen_->GetGraph()->GetArena();
+}
+
+#define __ codegen->GetAssembler()->
+
+static void MoveFromReturnRegister(Location trg,
+ Primitive::Type type,
+ CodeGeneratorMIPS* codegen) {
+ if (!trg.IsValid()) {
+ DCHECK_EQ(type, Primitive::kPrimVoid);
+ return;
+ }
+
+ DCHECK_NE(type, Primitive::kPrimVoid);
+
+ if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) {
+ Register trg_reg = trg.AsRegister<Register>();
+ if (trg_reg != V0) {
+ __ Move(V0, trg_reg);
+ }
+ } else {
+ FRegister trg_reg = trg.AsFpuRegister<FRegister>();
+ if (trg_reg != F0) {
+ if (type == Primitive::kPrimFloat) {
+ __ MovS(F0, trg_reg);
+ } else {
+ __ MovD(F0, trg_reg);
+ }
+ }
+ }
+}
+
+static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
+ InvokeDexCallingConventionVisitorMIPS calling_convention_visitor;
+ IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor);
+}
+
+// Slow-path for fallback (calling the managed code to handle the
+// intrinsic) in an intrinsified call. This will copy the arguments
+// into the positions for a regular call.
+//
+// Note: The actual parameters are required to be in the locations
+// given by the invoke's location summary. If an intrinsic
+// modifies those locations before a slowpath call, they must be
+// restored!
+class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS {
+ public:
+ explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : invoke_(invoke) { }
+
+ void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
+ CodeGeneratorMIPS* codegen = down_cast<CodeGeneratorMIPS*>(codegen_in);
+
+ __ Bind(GetEntryLabel());
+
+ SaveLiveRegisters(codegen, invoke_->GetLocations());
+
+ MoveArguments(invoke_, codegen);
+
+ if (invoke_->IsInvokeStaticOrDirect()) {
+ codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(),
+ Location::RegisterLocation(A0));
+ codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this);
+ } else {
+ UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented";
+ UNREACHABLE();
+ }
+
+ // Copy the result back to the expected output.
+ Location out = invoke_->GetLocations()->Out();
+ if (out.IsValid()) {
+ DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory.
+ DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
+ MoveFromReturnRegister(out, invoke_->GetType(), codegen);
+ }
+
+ RestoreLiveRegisters(codegen, invoke_->GetLocations());
+ __ B(GetExitLabel());
+ }
+
+ const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathMIPS"; }
+
+ private:
+ // The instruction where this slow path is happening.
+ HInvoke* const invoke_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathMIPS);
+};
+
+#undef __
+
+bool IntrinsicLocationsBuilderMIPS::TryDispatch(HInvoke* invoke) {
+ Dispatch(invoke);
+ LocationSummary* res = invoke->GetLocations();
+ return res != nullptr && res->Intrinsified();
+}
+
+#define __ assembler->
+
+// Unimplemented intrinsics.
+
+#define UNIMPLEMENTED_INTRINSIC(Name) \
+void IntrinsicLocationsBuilderMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+} \
+void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \
+}
+
+UNIMPLEMENTED_INTRINSIC(IntegerReverse)
+UNIMPLEMENTED_INTRINSIC(LongReverse)
+UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
+UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
+UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfLeadingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfLeadingZeros)
+UNIMPLEMENTED_INTRINSIC(FloatIntBitsToFloat)
+UNIMPLEMENTED_INTRINSIC(DoubleLongBitsToDouble)
+UNIMPLEMENTED_INTRINSIC(FloatFloatToRawIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToRawLongBits)
+UNIMPLEMENTED_INTRINSIC(MathAbsDouble)
+UNIMPLEMENTED_INTRINSIC(MathAbsFloat)
+UNIMPLEMENTED_INTRINSIC(MathAbsInt)
+UNIMPLEMENTED_INTRINSIC(MathAbsLong)
+UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
+UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
+UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
+UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
+UNIMPLEMENTED_INTRINSIC(MathMinIntInt)
+UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
+UNIMPLEMENTED_INTRINSIC(MathMaxIntInt)
+UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
+UNIMPLEMENTED_INTRINSIC(MathSqrt)
+UNIMPLEMENTED_INTRINSIC(MathCeil)
+UNIMPLEMENTED_INTRINSIC(MathFloor)
+UNIMPLEMENTED_INTRINSIC(MathRint)
+UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
+UNIMPLEMENTED_INTRINSIC(MathRoundFloat)
+UNIMPLEMENTED_INTRINSIC(MemoryPeekByte)
+UNIMPLEMENTED_INTRINSIC(MemoryPeekIntNative)
+UNIMPLEMENTED_INTRINSIC(MemoryPeekLongNative)
+UNIMPLEMENTED_INTRINSIC(MemoryPeekShortNative)
+UNIMPLEMENTED_INTRINSIC(MemoryPokeByte)
+UNIMPLEMENTED_INTRINSIC(MemoryPokeIntNative)
+UNIMPLEMENTED_INTRINSIC(MemoryPokeLongNative)
+UNIMPLEMENTED_INTRINSIC(MemoryPokeShortNative)
+UNIMPLEMENTED_INTRINSIC(ThreadCurrentThread)
+UNIMPLEMENTED_INTRINSIC(UnsafeGet)
+UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile)
+UNIMPLEMENTED_INTRINSIC(UnsafeGetLong)
+UNIMPLEMENTED_INTRINSIC(UnsafeGetLongVolatile)
+UNIMPLEMENTED_INTRINSIC(UnsafeGetObject)
+UNIMPLEMENTED_INTRINSIC(UnsafeGetObjectVolatile)
+UNIMPLEMENTED_INTRINSIC(UnsafePut)
+UNIMPLEMENTED_INTRINSIC(UnsafePutOrdered)
+UNIMPLEMENTED_INTRINSIC(UnsafePutVolatile)
+UNIMPLEMENTED_INTRINSIC(UnsafePutObject)
+UNIMPLEMENTED_INTRINSIC(UnsafePutObjectOrdered)
+UNIMPLEMENTED_INTRINSIC(UnsafePutObjectVolatile)
+UNIMPLEMENTED_INTRINSIC(UnsafePutLong)
+UNIMPLEMENTED_INTRINSIC(UnsafePutLongOrdered)
+UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
+UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
+UNIMPLEMENTED_INTRINSIC(StringCharAt)
+UNIMPLEMENTED_INTRINSIC(StringCompareTo)
+UNIMPLEMENTED_INTRINSIC(StringEquals)
+UNIMPLEMENTED_INTRINSIC(StringIndexOf)
+UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter)
+UNIMPLEMENTED_INTRINSIC(StringNewStringFromBytes)
+UNIMPLEMENTED_INTRINSIC(StringNewStringFromChars)
+UNIMPLEMENTED_INTRINSIC(StringNewStringFromString)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+
+UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
+UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
+
+#undef UNIMPLEMENTED_INTRINSIC
+
+#undef __
+
+} // namespace mips
+} // namespace art
diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h
new file mode 100644
index 0000000000..c71b3c68b7
--- /dev/null
+++ b/compiler/optimizing/intrinsics_mips.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_MIPS_H_
+#define ART_COMPILER_OPTIMIZING_INTRINSICS_MIPS_H_
+
+#include "intrinsics.h"
+
+namespace art {
+
+class ArenaAllocator;
+class HInvokeStaticOrDirect;
+class HInvokeVirtual;
+
+namespace mips {
+
+class CodeGeneratorMIPS;
+class MipsAssembler;
+
+class IntrinsicLocationsBuilderMIPS FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicLocationsBuilderMIPS(CodeGeneratorMIPS* codegen);
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether
+ // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to
+ // the invoke.
+ bool TryDispatch(HInvoke* invoke);
+
+ private:
+ ArenaAllocator* arena_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderMIPS);
+};
+
+class IntrinsicCodeGeneratorMIPS FINAL : public IntrinsicVisitor {
+ public:
+ explicit IntrinsicCodeGeneratorMIPS(CodeGeneratorMIPS* codegen) : codegen_(codegen) {}
+
+ // Define visitor methods.
+
+#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \
+ void Visit ## Name(HInvoke* invoke) OVERRIDE;
+#include "intrinsics_list.h"
+INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+#undef INTRINSICS_LIST
+#undef OPTIMIZING_INTRINSICS
+
+ private:
+ MipsAssembler* GetAssembler();
+
+ ArenaAllocator* GetAllocator();
+
+ CodeGeneratorMIPS* codegen_;
+
+ DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorMIPS);
+};
+
+} // namespace mips
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_MIPS_H_
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 56c4177b29..05c7eb02d9 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -272,7 +272,9 @@ void IntrinsicCodeGeneratorMIPS64::VisitShortReverseBytes(HInvoke* invoke) {
GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler());
}
-static void GenNumberOfLeadingZeroes(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
+static void GenNumberOfLeadingZeroes(LocationSummary* locations,
+ bool is64bit,
+ Mips64Assembler* assembler) {
GpuRegister in = locations->InAt(0).AsRegister<GpuRegister>();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
@@ -301,7 +303,9 @@ void IntrinsicCodeGeneratorMIPS64::VisitLongNumberOfLeadingZeros(HInvoke* invoke
GenNumberOfLeadingZeroes(invoke->GetLocations(), true, GetAssembler());
}
-static void GenNumberOfTrailingZeroes(LocationSummary* locations, bool is64bit, Mips64Assembler* assembler) {
+static void GenNumberOfTrailingZeroes(LocationSummary* locations,
+ bool is64bit,
+ Mips64Assembler* assembler) {
Location in = locations->InAt(0);
Location out = locations->Out();
@@ -383,7 +387,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateRight(HInvoke* invoke) {
GenRotateRight(invoke, Primitive::kPrimInt, GetAssembler());
}
-// int java.lang.Long.rotateRight(long i, int distance)
+// long java.lang.Long.rotateRight(long i, int distance)
void IntrinsicLocationsBuilderMIPS64::VisitLongRotateRight(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
LocationSummary::kNoCall,
@@ -446,7 +450,7 @@ void IntrinsicCodeGeneratorMIPS64::VisitIntegerRotateLeft(HInvoke* invoke) {
GenRotateLeft(invoke, Primitive::kPrimInt, GetAssembler());
}
-// int java.lang.Long.rotateLeft(long i, int distance)
+// long java.lang.Long.rotateLeft(long i, int distance)
void IntrinsicLocationsBuilderMIPS64::VisitLongRotateLeft(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
LocationSummary::kNoCall,
@@ -754,17 +758,19 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathSqrt(HInvoke* invoke) {
__ SqrtD(out, in);
}
-static void CreateFPToFP(ArenaAllocator* arena, HInvoke* invoke) {
+static void CreateFPToFP(ArenaAllocator* arena,
+ HInvoke* invoke,
+ Location::OutputOverlap overlaps = Location::kOutputOverlap) {
LocationSummary* locations = new (arena) LocationSummary(invoke,
LocationSummary::kNoCall,
kIntrinsified);
locations->SetInAt(0, Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+ locations->SetOut(Location::RequiresFpuRegister(), overlaps);
}
// double java.lang.Math.rint(double)
void IntrinsicLocationsBuilderMIPS64::VisitMathRint(HInvoke* invoke) {
- CreateFPToFP(arena_, invoke);
+ CreateFPToFP(arena_, invoke, Location::kNoOutputOverlap);
}
void IntrinsicCodeGeneratorMIPS64::VisitMathRint(HInvoke* invoke) {
@@ -788,15 +794,22 @@ const constexpr uint16_t kFPLeaveUnchanged = kPositiveZero |
kQuietNaN |
kSignalingNaN;
-void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
- Mips64Assembler* assembler = GetAssembler();
+enum FloatRoundingMode {
+ kFloor,
+ kCeil,
+};
+
+static void GenRoundingMode(LocationSummary* locations,
+ FloatRoundingMode mode,
+ Mips64Assembler* assembler) {
FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
+ DCHECK_NE(in, out);
+
Label done;
- // double floor(double in) {
+ // double floor/ceil(double in) {
// if in.isNaN || in.isInfinite || in.isZero {
// return in;
// }
@@ -806,19 +819,23 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
__ MovD(out, in);
__ Bnezc(AT, &done);
- // Long outLong = floor(in);
+ // Long outLong = floor/ceil(in);
// if outLong == Long.MAX_VALUE {
- // // floor() has almost certainly returned a value which
- // // can't be successfully represented as a signed 64-bit
- // // number. Java expects that the input value will be
- // // returned in these cases.
- // // There is also a small probability that floor(in)
- // // correctly truncates the input value to Long.MAX_VALUE. In
- // // that case, this exception handling code still does the
- // // correct thing.
+ // // floor()/ceil() has almost certainly returned a value
+ // // which can't be successfully represented as a signed
+ // // 64-bit number. Java expects that the input value will
+ // // be returned in these cases.
+ // // There is also a small probability that floor(in)/ceil(in)
+ // // correctly truncates/rounds up the input value to
+ // // Long.MAX_VALUE. In that case, this exception handling
+ // // code still does the correct thing.
// return in;
// }
- __ FloorLD(out, in);
+ if (mode == kFloor) {
+ __ FloorLD(out, in);
+ } else if (mode == kCeil) {
+ __ CeilLD(out, in);
+ }
__ Dmfc1(AT, out);
__ MovD(out, in);
__ LoadConst64(TMP, kPrimLongMax);
@@ -832,53 +849,17 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
// }
}
+void IntrinsicCodeGeneratorMIPS64::VisitMathFloor(HInvoke* invoke) {
+ GenRoundingMode(invoke->GetLocations(), kFloor, GetAssembler());
+}
+
// double java.lang.Math.ceil(double)
void IntrinsicLocationsBuilderMIPS64::VisitMathCeil(HInvoke* invoke) {
CreateFPToFP(arena_, invoke);
}
void IntrinsicCodeGeneratorMIPS64::VisitMathCeil(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
- Mips64Assembler* assembler = GetAssembler();
- FpuRegister in = locations->InAt(0).AsFpuRegister<FpuRegister>();
- FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
-
- Label done;
-
- // double ceil(double in) {
- // if in.isNaN || in.isInfinite || in.isZero {
- // return in;
- // }
- __ ClassD(out, in);
- __ Dmfc1(AT, out);
- __ Andi(AT, AT, kFPLeaveUnchanged); // +0.0 | +Inf | -0.0 | -Inf | qNaN | sNaN
- __ MovD(out, in);
- __ Bnezc(AT, &done);
-
- // Long outLong = ceil(in);
- // if outLong == Long.MAX_VALUE {
- // // ceil() has almost certainly returned a value which
- // // can't be successfully represented as a signed 64-bit
- // // number. Java expects that the input value will be
- // // returned in these cases.
- // // There is also a small probability that ceil(in)
- // // correctly rounds up the input value to Long.MAX_VALUE. In
- // // that case, this exception handling code still does the
- // // correct thing.
- // return in;
- // }
- __ CeilLD(out, in);
- __ Dmfc1(AT, out);
- __ MovD(out, in);
- __ LoadConst64(TMP, kPrimLongMax);
- __ Beqc(AT, TMP, &done);
-
- // double out = outLong;
- // return out;
- __ Dmtc1(AT, out);
- __ Cvtdl(out, out);
- __ Bind(&done);
- // }
+ GenRoundingMode(invoke->GetLocations(), kCeil, GetAssembler());
}
// byte libcore.io.Memory.peekByte(long address)
@@ -1246,6 +1227,91 @@ void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLongVolatile(HInvoke* invoke) {
GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_);
}
+static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->SetOut(Location::RequiresRegister());
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorMIPS64* codegen) {
+ Mips64Assembler* assembler = codegen->GetAssembler();
+ GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>();
+ GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>();
+ GpuRegister expected = locations->InAt(3).AsRegister<GpuRegister>();
+ GpuRegister value = locations->InAt(4).AsRegister<GpuRegister>();
+ GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+
+ DCHECK_NE(base, out);
+ DCHECK_NE(offset, out);
+ DCHECK_NE(expected, out);
+
+ // do {
+ // tmp_value = [tmp_ptr] - expected;
+ // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
+ // result = tmp_value != 0;
+
+ Label loop_head, exit_loop;
+ __ Daddu(TMP, base, offset);
+ __ Sync(0);
+ __ Bind(&loop_head);
+ if (type == Primitive::kPrimLong) {
+ __ Lld(out, TMP);
+ } else {
+ __ Ll(out, TMP);
+ }
+ __ Dsubu(out, out, expected); // If we didn't get the 'expected'
+ __ Sltiu(out, out, 1); // value, set 'out' to false, and
+ __ Beqzc(out, &exit_loop); // return.
+ __ Move(out, value); // Use 'out' for the 'store conditional' instruction.
+ // If we use 'value' directly, we would lose 'value'
+ // in the case that the store fails. Whether the
+ // store succeeds, or fails, it will load the
+ // correct boolean value into the 'out' register.
+ if (type == Primitive::kPrimLong) {
+ __ Scd(out, TMP);
+ } else {
+ __ Sc(out, TMP);
+ }
+ __ Beqzc(out, &loop_head); // If we couldn't do the read-modify-write
+ // cycle atomically then retry.
+ __ Bind(&exit_loop);
+ __ Sync(0);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x)
+void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASInt(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASInt(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapLong(Object o, long offset, long expected, long x)
+void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASLong(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASLong(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x)
+void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASObject(HInvoke* invoke) {
+ CreateIntIntIntIntIntToInt(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASObject(HInvoke* invoke) {
+ GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
// char java.lang.String.charAt(int index)
void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -1521,9 +1587,6 @@ void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE
UNIMPLEMENTED_INTRINSIC(MathRoundDouble)
UNIMPLEMENTED_INTRINSIC(MathRoundFloat)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASInt)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASLong)
-UNIMPLEMENTED_INTRINSIC(UnsafeCASObject)
UNIMPLEMENTED_INTRINSIC(StringEquals)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 90f28e511e..6fbb6823d6 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -59,7 +59,7 @@ class ReferenceInfo : public ArenaObject<kArenaAllocMisc> {
(use->IsInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
(use->IsUnresolvedInstanceFieldSet() && (reference_ == use->InputAt(1))) ||
(use->IsStaticFieldSet() && (reference_ == use->InputAt(1))) ||
- (use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(1))) ||
+ (use->IsUnresolvedStaticFieldSet() && (reference_ == use->InputAt(0))) ||
(use->IsArraySet() && (reference_ == use->InputAt(2)))) {
// reference_ is merged to a phi, passed to a callee, or stored to heap.
// reference_ isn't the only name that can refer to its value anymore.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 98c3096cae..68fb0acf7f 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -574,6 +574,17 @@ bool HLoopInformation::IsIn(const HLoopInformation& other) const {
return other.blocks_.IsBitSet(header_->GetBlockId());
}
+bool HLoopInformation::IsLoopInvariant(HInstruction* instruction, bool must_dominate) const {
+ HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation();
+ if (other_loop != this && (other_loop == nullptr || !other_loop->IsIn(*this))) {
+ if (must_dominate) {
+ return instruction->GetBlock()->Dominates(GetHeader());
+ }
+ return true;
+ }
+ return false;
+}
+
size_t HLoopInformation::GetLifetimeEnd() const {
size_t last_position = 0;
for (HBasicBlock* back_edge : GetBackEdges()) {
@@ -1641,7 +1652,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
// Update the meta information surrounding blocks:
// (1) the graph they are now in,
// (2) the reverse post order of that graph,
- // (3) the potential loop information they are now in.
+ // (3) the potential loop information they are now in,
+ // (4) try block membership.
// We don't add the entry block, the exit block, and the first block, which
// has been merged with `at`.
@@ -1657,41 +1669,47 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
size_t index_of_at = IndexOfElement(outer_graph->reverse_post_order_, at);
MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
- // Do a reverse post order of the blocks in the callee and do (1), (2),
- // and (3) to the blocks that apply.
- HLoopInformation* info = at->GetLoopInformation();
+ HLoopInformation* loop_info = at->GetLoopInformation();
+ // Copy TryCatchInformation if `at` is a try block, not if it is a catch block.
+ TryCatchInformation* try_catch_info = at->IsTryBlock() ? at->GetTryCatchInformation() : nullptr;
+
+ // Do a reverse post order of the blocks in the callee and do (1), (2), (3)
+ // and (4) to the blocks that apply.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
HBasicBlock* current = it.Current();
if (current != exit_block_ && current != entry_block_ && current != first) {
DCHECK(!current->IsInLoop());
+ DCHECK(current->GetTryCatchInformation() == nullptr);
DCHECK(current->GetGraph() == this);
current->SetGraph(outer_graph);
outer_graph->AddBlock(current);
outer_graph->reverse_post_order_[++index_of_at] = current;
- if (info != nullptr) {
- current->SetLoopInformation(info);
+ if (loop_info != nullptr) {
+ current->SetLoopInformation(loop_info);
for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
loop_it.Current()->Add(current);
}
}
+ current->SetTryCatchInformation(try_catch_info);
}
}
- // Do (1), (2), and (3) to `to`.
+ // Do (1), (2), (3) and (4) to `to`.
to->SetGraph(outer_graph);
outer_graph->AddBlock(to);
outer_graph->reverse_post_order_[++index_of_at] = to;
- if (info != nullptr) {
- to->SetLoopInformation(info);
+ if (loop_info != nullptr) {
+ to->SetLoopInformation(loop_info);
for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
loop_it.Current()->Add(to);
}
- if (info->IsBackEdge(*at)) {
+ if (loop_info->IsBackEdge(*at)) {
// Only `to` can become a back edge, as the inlined blocks
// are predecessors of `to`.
- info->ReplaceBackEdge(at, to);
+ loop_info->ReplaceBackEdge(at, to);
}
}
+ to->SetTryCatchInformation(try_catch_info);
}
// Update the next instruction id of the outer graph, so that instructions
@@ -1911,8 +1929,8 @@ bool HInvoke::NeedsEnvironment() const {
return !opt.GetDoesNotNeedEnvironment();
}
-bool HInvokeStaticOrDirect::NeedsDexCache() const {
- if (IsRecursive() || IsStringInit()) {
+bool HInvokeStaticOrDirect::NeedsDexCacheOfDeclaringClass() const {
+ if (GetMethodLoadKind() != MethodLoadKind::kDexCacheViaMethod) {
return false;
}
if (!IsIntrinsic()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7aa933ddf0..0f2c1cffee 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -551,6 +551,12 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> {
// Note that `other` *must* be populated before entering this function.
bool IsIn(const HLoopInformation& other) const;
+ // Returns true if instruction is not defined within this loop or any loop nested inside
+ // this loop. If must_dominate is set, only definitions that actually dominate the loop
+ // header can be invariant. Otherwise, any definition outside the loop, including
+ // definitions that appear after the loop, is invariant.
+ bool IsLoopInvariant(HInstruction* instruction, bool must_dominate) const;
+
const ArenaBitVector& GetBlocks() const { return blocks_; }
void Add(HBasicBlock* block);
@@ -1980,7 +1986,9 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
return NeedsEnvironment() || IsLoadClass() || IsLoadString();
}
- virtual bool NeedsDexCache() const { return false; }
+ // Returns whether the code generation of the instruction will require to have access
+ // to the dex cache of the current method's declaring class via the current method.
+ virtual bool NeedsDexCacheOfDeclaringClass() const { return false; }
// Does this instruction have any use in an environment before
// control flow hits 'other'?
@@ -3368,15 +3376,15 @@ class HInvokeStaticOrDirect : public HInvoke {
};
struct DispatchInfo {
- const MethodLoadKind method_load_kind;
- const CodePtrLocation code_ptr_location;
+ MethodLoadKind method_load_kind;
+ CodePtrLocation code_ptr_location;
// The method load data holds
// - thread entrypoint offset for kStringInit method if this is a string init invoke.
// Note that there are multiple string init methods, each having its own offset.
// - the method address for kDirectAddress
// - the dex cache arrays offset for kDexCachePcRel.
- const uint64_t method_load_data;
- const uint64_t direct_code_ptr;
+ uint64_t method_load_data;
+ uint64_t direct_code_ptr;
};
HInvokeStaticOrDirect(ArenaAllocator* arena,
@@ -3405,6 +3413,10 @@ class HInvokeStaticOrDirect : public HInvoke {
target_method_(target_method),
dispatch_info_(dispatch_info) {}
+ void SetDispatchInfo(const DispatchInfo& dispatch_info) {
+ dispatch_info_ = dispatch_info;
+ }
+
bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE {
// We access the method via the dex cache so we can't do an implicit null check.
// TODO: for intrinsics we can generate implicit null checks.
@@ -3419,11 +3431,13 @@ class HInvokeStaticOrDirect : public HInvoke {
MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
CodePtrLocation GetCodePtrLocation() const { return dispatch_info_.code_ptr_location; }
bool IsRecursive() const { return GetMethodLoadKind() == MethodLoadKind::kRecursive; }
- bool NeedsDexCache() const OVERRIDE;
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE;
bool IsStringInit() const { return GetMethodLoadKind() == MethodLoadKind::kStringInit; }
uint32_t GetCurrentMethodInputIndex() const { return GetNumberOfArguments(); }
bool HasMethodAddress() const { return GetMethodLoadKind() == MethodLoadKind::kDirectAddress; }
- bool HasPcRelDexCache() const { return GetMethodLoadKind() == MethodLoadKind::kDexCachePcRelative; }
+ bool HasPcRelDexCache() const {
+ return GetMethodLoadKind() == MethodLoadKind::kDexCachePcRelative;
+ }
bool HasDirectCodePtr() const { return GetCodePtrLocation() == CodePtrLocation::kCallDirect; }
MethodReference GetTargetMethod() const { return target_method_; }
@@ -4736,6 +4750,9 @@ class HLoadClass : public HExpression<1> {
return generate_clinit_check_;
}
void SetMustGenerateClinitCheck(bool generate_clinit_check) {
+ // The entrypoint the code generator is going to call does not do
+ // clinit of the class.
+ DCHECK(!NeedsAccessCheck());
generate_clinit_check_ = generate_clinit_check;
}
@@ -4765,7 +4782,7 @@ class HLoadClass : public HExpression<1> {
const DexFile& GetDexFile() { return dex_file_; }
- bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
@@ -4807,7 +4824,7 @@ class HLoadString : public HExpression<1> {
// TODO: Can we deopt or debug when we resolve a string?
bool NeedsEnvironment() const OVERRIDE { return false; }
- bool NeedsDexCache() const OVERRIDE { return true; }
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
static SideEffects SideEffectsForArchRuntimeCalls() {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index d6f2543890..8cb2cfc816 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -63,6 +63,7 @@
#include "prepare_for_register_allocation.h"
#include "reference_type_propagation.h"
#include "register_allocator.h"
+#include "sharpening.h"
#include "side_effects_analysis.h"
#include "ssa_builder.h"
#include "ssa_phi_elimination.h"
@@ -378,6 +379,7 @@ static void RunOptimizations(HOptimization* optimizations[],
}
static void MaybeRunInliner(HGraph* graph,
+ CodeGenerator* codegen,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
const DexCompilationUnit& dex_compilation_unit,
@@ -392,7 +394,7 @@ static void MaybeRunInliner(HGraph* graph,
ArenaAllocator* arena = graph->GetArena();
HInliner* inliner = new (arena) HInliner(
- graph, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
+ graph, codegen, dex_compilation_unit, dex_compilation_unit, driver, handles, stats);
ReferenceTypePropagation* type_propagation =
new (arena) ReferenceTypePropagation(graph, handles,
"reference_type_propagation_after_inlining");
@@ -445,6 +447,7 @@ static void RunArchOptimizations(InstructionSet instruction_set,
}
static void RunOptimizations(HGraph* graph,
+ CodeGenerator* codegen,
CompilerDriver* driver,
OptimizingCompilerStats* stats,
const DexCompilationUnit& dex_compilation_unit,
@@ -467,6 +470,7 @@ static void RunOptimizations(HGraph* graph,
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, induction);
ReferenceTypePropagation* type_propagation =
new (arena) ReferenceTypePropagation(graph, handles);
+ HSharpening* sharpening = new (arena) HSharpening(graph, codegen, dex_compilation_unit, driver);
InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_after_types");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
@@ -481,12 +485,15 @@ static void RunOptimizations(HGraph* graph,
fold1,
simplify1,
type_propagation,
+ sharpening,
dce1,
simplify2
};
RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer);
+ MaybeRunInliner(graph, codegen, driver, stats, dex_compilation_unit, pass_observer, handles);
+
// TODO: Update passes incompatible with try/catch so we have the same
// pipeline for all methods.
if (graph->HasTryCatch()) {
@@ -502,8 +509,6 @@ static void RunOptimizations(HGraph* graph,
RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer);
} else {
- 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.
@@ -577,8 +582,13 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
ScopedObjectAccess soa(Thread::Current());
StackHandleScopeCollection handles(soa.Self());
soa.Self()->TransitionFromRunnableToSuspended(kNative);
- RunOptimizations(graph, compiler_driver, compilation_stats_.get(),
- dex_compilation_unit, pass_observer, &handles);
+ RunOptimizations(graph,
+ codegen,
+ compiler_driver,
+ compilation_stats_.get(),
+ dex_compilation_unit,
+ pass_observer,
+ &handles);
AllocateRegisters(graph, codegen, pass_observer);
@@ -607,7 +617,7 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph,
codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
- &src_mapping_table,
+ ArrayRef<const SrcMapElem>(src_mapping_table),
ArrayRef<const uint8_t>(), // mapping_table.
ArrayRef<const uint8_t>(stack_map),
ArrayRef<const uint8_t>(), // native_gc_map.
@@ -652,7 +662,7 @@ CompiledMethod* OptimizingCompiler::CompileBaseline(
codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(),
codegen->GetCoreSpillMask(),
codegen->GetFpuSpillMask(),
- &src_mapping_table,
+ ArrayRef<const SrcMapElem>(src_mapping_table),
AlignVectorSize(mapping_table),
AlignVectorSize(vmap_table),
AlignVectorSize(gc_map),
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 26a05da4cb..659da068a9 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -373,12 +373,18 @@ void RTPVisitor::SetClassAsTypeInfo(HInstruction* instr,
if (instr->IsInvokeStaticOrDirect() && instr->AsInvokeStaticOrDirect()->IsStringInit()) {
// Calls to String.<init> are replaced with a StringFactory.
if (kIsDebugBuild) {
- ScopedObjectAccess soa(Thread::Current());
+ HInvoke* invoke = instr->AsInvoke();
ClassLinker* cl = Runtime::Current()->GetClassLinker();
- mirror::DexCache* dex_cache = cl->FindDexCache(
- soa.Self(), instr->AsInvoke()->GetDexFile(), false);
- ArtMethod* method = dex_cache->GetResolvedMethod(
- instr->AsInvoke()->GetDexMethodIndex(), cl->GetImagePointerSize());
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(cl->FindDexCache(soa.Self(), invoke->GetDexFile(), false)));
+ // Use a null loader. We should probably use the compiling method's class loader,
+ // but then we would need to pass it to RTPVisitor just for this debug check. Since
+ // the method is from the String class, the null loader is good enough.
+ Handle<mirror::ClassLoader> loader;
+ ArtMethod* method = cl->ResolveMethod(
+ invoke->GetDexFile(), invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
DCHECK(method != nullptr);
mirror::Class* declaring_class = method->GetDeclaringClass();
DCHECK(declaring_class != nullptr);
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
new file mode 100644
index 0000000000..a128079cdb
--- /dev/null
+++ b/compiler/optimizing/sharpening.cc
@@ -0,0 +1,142 @@
+/*
+ * 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 "sharpening.h"
+
+#include "code_generator.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+#include "driver/compiler_driver.h"
+#include "nodes.h"
+#include "runtime.h"
+
+namespace art {
+
+void HSharpening::Run() {
+ // We don't care about the order of the blocks here.
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsInvokeStaticOrDirect()) {
+ ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect());
+ }
+ // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder
+ // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data
+ // because we know the type better when inlining.
+ // TODO: HLoadClass, HLoadString - select PC relative dex cache array access if
+ // available.
+ }
+ }
+}
+
+void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ if (invoke->IsStringInit()) {
+ // Not using the dex cache arrays. But we could still try to use a better dispatch...
+ // TODO: Use direct_method and direct_code for the appropriate StringFactory method.
+ return;
+ }
+
+ // TODO: Avoid CompilerDriver.
+ InvokeType invoke_type = invoke->GetOriginalInvokeType();
+ MethodReference target_method(&graph_->GetDexFile(), invoke->GetDexMethodIndex());
+ int vtable_idx;
+ uintptr_t direct_code, direct_method;
+ bool success = compiler_driver_->ComputeInvokeInfo(
+ &compilation_unit_,
+ invoke->GetDexPc(),
+ false /* update_stats: already updated in builder */,
+ true /* enable_devirtualization */,
+ &invoke_type,
+ &target_method,
+ &vtable_idx,
+ &direct_code,
+ &direct_method);
+ DCHECK(success);
+ DCHECK_EQ(invoke_type, invoke->GetInvokeType());
+ DCHECK_EQ(target_method.dex_file, invoke->GetTargetMethod().dex_file);
+ DCHECK_EQ(target_method.dex_method_index, invoke->GetTargetMethod().dex_method_index);
+
+ HInvokeStaticOrDirect::MethodLoadKind method_load_kind;
+ HInvokeStaticOrDirect::CodePtrLocation code_ptr_location;
+ uint64_t method_load_data = 0u;
+ uint64_t direct_code_ptr = 0u;
+
+ HGraph* outer_graph = codegen_->GetGraph();
+ if (target_method.dex_file == &outer_graph->GetDexFile() &&
+ target_method.dex_method_index == outer_graph->GetMethodIdx()) {
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive;
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf;
+ } else {
+ bool use_pc_relative_instructions =
+ ((direct_method == 0u || direct_code == static_cast<uintptr_t>(-1))) &&
+ ContainsElement(compiler_driver_->GetDexFilesForOatFile(), target_method.dex_file);
+ if (direct_method != 0u) { // Should we use a direct pointer to the method?
+ // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while
+ // kDirectAddress would be fine for image methods, we don't support it at the moment.
+ DCHECK(!Runtime::Current()->UseJit());
+ if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now?
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress;
+ method_load_data = direct_method;
+ } else { // The direct pointer will be known at link time.
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup;
+ }
+ } else { // Use dex cache.
+ DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile());
+ if (use_pc_relative_instructions) { // Can we use PC-relative access to the dex cache arrays?
+ DCHECK(!Runtime::Current()->UseJit());
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative;
+ DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()),
+ &graph_->GetDexFile());
+ method_load_data = layout.MethodOffset(target_method.dex_method_index);
+ } else { // We must go through the ArtMethod's pointer to resolved methods.
+ method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod;
+ }
+ }
+ if (direct_code != 0u) { // Should we use a direct pointer to the code?
+ // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and
+ // while kCallDirect would be fine for image methods, we don't support it at the moment.
+ DCHECK(!Runtime::Current()->UseJit());
+ if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now?
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect;
+ direct_code_ptr = direct_code;
+ } else if (use_pc_relative_instructions) {
+ // Use PC-relative calls for invokes within a multi-dex oat file.
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative;
+ } else { // The direct pointer will be known at link time.
+ // NOTE: This is used for app->boot calls when compiling an app against
+ // a relocatable but not yet relocated image.
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup;
+ }
+ } else { // We must use the code pointer from the ArtMethod.
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+ }
+ }
+
+ if (graph_->IsDebuggable()) {
+ // For debuggable apps always use the code pointer from ArtMethod
+ // so that we don't circumvent instrumentation stubs if installed.
+ code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
+ }
+
+ HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = {
+ method_load_kind, code_ptr_location, method_load_data, direct_code_ptr
+ };
+ HInvokeStaticOrDirect::DispatchInfo dispatch_info =
+ codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info,
+ invoke->GetTargetMethod());
+ invoke->SetDispatchInfo(dispatch_info);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
new file mode 100644
index 0000000000..adae7007dd
--- /dev/null
+++ b/compiler/optimizing/sharpening.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_SHARPENING_H_
+#define ART_COMPILER_OPTIMIZING_SHARPENING_H_
+
+#include "optimization.h"
+
+namespace art {
+
+class CodeGenerator;
+class CompilerDriver;
+class DexCompilationUnit;
+class HInvokeStaticOrDirect;
+
+// Optimization that tries to improve the way we dispatch methods and access types,
+// fields, etc. Besides actual method sharpening based on receiver type (for example
+// virtual->direct), this includes selecting the best available dispatch for
+// invoke-static/-direct based on code generator support.
+class HSharpening : public HOptimization {
+ public:
+ HSharpening(HGraph* graph,
+ CodeGenerator* codegen,
+ const DexCompilationUnit& compilation_unit,
+ CompilerDriver* compiler_driver)
+ : HOptimization(graph, kSharpeningPassName),
+ codegen_(codegen),
+ compilation_unit_(compilation_unit),
+ compiler_driver_(compiler_driver) { }
+
+ void Run() OVERRIDE;
+
+ static constexpr const char* kSharpeningPassName = "sharpening";
+
+ private:
+ void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
+
+ CodeGenerator* codegen_;
+ const DexCompilationUnit& compilation_unit_;
+ CompilerDriver* compiler_driver_;
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_SHARPENING_H_
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 807bedaa04..68e39568bb 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -16,6 +16,8 @@
#include "assembler_arm.h"
+#include <algorithm>
+
#include "base/bit_utils.h"
#include "base/logging.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -922,5 +924,24 @@ uint32_t ArmAssembler::ModifiedImmediate(uint32_t value) {
return value | i << 26 | imm3 << 12 | a << 7;
}
+void ArmAssembler::FinalizeTrackedLabels() {
+ if (!tracked_labels_.empty()) {
+ // This array should be sorted, as assembly is generated in linearized order. It isn't
+ // technically required, but GetAdjustedPosition() used in AdjustLabelPosition() can take
+ // advantage of it. So ensure that it's actually the case.
+ DCHECK(std::is_sorted(
+ tracked_labels_.begin(),
+ tracked_labels_.end(),
+ [](const Label* lhs, const Label* rhs) { return lhs->Position() < rhs->Position(); }));
+
+ Label* last_label = nullptr; // Track duplicates, we must not adjust twice.
+ for (Label* label : tracked_labels_) {
+ DCHECK_NE(label, last_label);
+ AdjustLabelPosition(label);
+ last_label = label;
+ }
+ }
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index d59bc6be40..4a6e6d7c3f 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -77,6 +77,45 @@ class Literal {
DISALLOW_COPY_AND_ASSIGN(Literal);
};
+// Jump table: table of labels emitted after the literals. Similar to literals.
+class JumpTable {
+ public:
+ explicit JumpTable(std::vector<Label*>&& labels)
+ : label_(), anchor_label_(), labels_(std::move(labels)) {
+ }
+
+ uint32_t GetSize() const {
+ return static_cast<uint32_t>(labels_.size()) * sizeof(uint32_t);
+ }
+
+ const std::vector<Label*>& GetData() const {
+ return labels_;
+ }
+
+ Label* GetLabel() {
+ return &label_;
+ }
+
+ const Label* GetLabel() const {
+ return &label_;
+ }
+
+ Label* GetAnchorLabel() {
+ return &anchor_label_;
+ }
+
+ const Label* GetAnchorLabel() const {
+ return &anchor_label_;
+ }
+
+ private:
+ Label label_;
+ Label anchor_label_;
+ std::vector<Label*> labels_;
+
+ DISALLOW_COPY_AND_ASSIGN(JumpTable);
+};
+
class ShifterOperand {
public:
ShifterOperand() : type_(kUnknown), rm_(kNoRegister), rs_(kNoRegister),
@@ -685,6 +724,8 @@ class ArmAssembler : public Assembler {
AddConstant(rd, rd, value, cond, set_cc);
}
+ virtual void CmpConstant(Register rn, int32_t value, Condition cond = AL) = 0;
+
// Load and Store. May clobber IP.
virtual void LoadImmediate(Register rd, int32_t value, Condition cond = AL) = 0;
void LoadSImmediate(SRegister sd, float value, Condition cond = AL) {
@@ -996,11 +1037,43 @@ class ArmAssembler : public Assembler {
b(label);
}
+ // Jump table support. This is split into three functions:
+ //
+ // * CreateJumpTable creates the internal metadata to track the jump targets, and emits code to
+ // load the base address of the jump table.
+ //
+ // * EmitJumpTableDispatch emits the code to actually jump, assuming that the right table value
+ // has been loaded into a register already.
+ //
+ // * FinalizeTables emits the jump table into the literal pool. This can only be called after the
+ // labels for the jump targets have been finalized.
+
+ // Create a jump table for the given labels that will be emitted when finalizing. Create a load
+ // sequence (or placeholder) that stores the base address into the given register. When the table
+ // is emitted, offsets will be relative to the location EmitJumpTableDispatch was called on (the
+ // anchor).
+ virtual JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) = 0;
+
+ // Emit the jump-table jump, assuming that the right value was loaded into displacement_reg.
+ virtual void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) = 0;
+
+ // Bind a Label that needs to be updated by the assembler in FinalizeCode() if its position
+ // changes due to branch/literal fixup.
+ void BindTrackedLabel(Label* label) {
+ Bind(label);
+ tracked_labels_.push_back(label);
+ }
+
protected:
// Returns whether or not the given register is used for passing parameters.
static int RegisterCompare(const Register* reg1, const Register* reg2) {
return *reg1 - *reg2;
}
+
+ void FinalizeTrackedLabels();
+
+ // Tracked labels. Use a vector, as we need to sort before adjusting.
+ std::vector<Label*> tracked_labels_;
};
// Slowpath entered when Thread::Current()->_exception is non-null
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 6e7c828b4a..a7dbacd3a9 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1385,6 +1385,21 @@ void Arm32Assembler::AddConstant(Register rd, Register rn, int32_t value,
}
}
+void Arm32Assembler::CmpConstant(Register rn, int32_t value, Condition cond) {
+ ShifterOperand shifter_op;
+ if (ShifterOperandCanHoldArm32(value, &shifter_op)) {
+ cmp(rn, shifter_op, cond);
+ } else if (ShifterOperandCanHoldArm32(~value, &shifter_op)) {
+ cmn(rn, shifter_op, cond);
+ } else {
+ movw(IP, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(IP, value_high, cond);
+ }
+ cmp(rn, ShifterOperand(IP), cond);
+ }
+}
void Arm32Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
ShifterOperand shifter_op;
@@ -1584,6 +1599,23 @@ void Arm32Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
b(label, NE);
}
+JumpTable* Arm32Assembler::CreateJumpTable(std::vector<Label*>&& labels ATTRIBUTE_UNUSED,
+ Register base_reg ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "CreateJumpTable is not supported on ARM32";
+ UNREACHABLE();
+}
+
+void Arm32Assembler::EmitJumpTableDispatch(JumpTable* jump_table ATTRIBUTE_UNUSED,
+ Register displacement_reg ATTRIBUTE_UNUSED) {
+ LOG(FATAL) << "EmitJumpTableDispatch is not supported on ARM32";
+ UNREACHABLE();
+}
+
+void Arm32Assembler::FinalizeCode() {
+ ArmAssembler::FinalizeCode();
+ // Currently the arm32 assembler does not support fixups, and thus no tracking. We must not call
+ // FinalizeTrackedLabels(), which would lead to an abort.
+}
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index 4646538716..5233dcbbb0 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -261,6 +261,8 @@ class Arm32Assembler FINAL : public ArmAssembler {
void AddConstant(Register rd, Register rn, int32_t value,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+ void CmpConstant(Register rn, int32_t value, Condition cond = AL) OVERRIDE;
+
// Load and Store. May clobber IP.
void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
void MarkExceptionHandler(Label* label) OVERRIDE;
@@ -308,6 +310,11 @@ class Arm32Assembler FINAL : public ArmAssembler {
void MemoryBarrier(ManagedRegister scratch) OVERRIDE;
+ JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) OVERRIDE;
+ void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) OVERRIDE;
+
+ void FinalizeCode() OVERRIDE;
+
private:
void EmitType01(Condition cond,
int type,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index cc87856e82..fb3aa1ea85 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -92,7 +92,7 @@ void Thumb2Assembler::BindLabel(Label* label, uint32_t bound_pc) {
label->BindTo(bound_pc);
}
-void Thumb2Assembler::BindLiterals() {
+uint32_t Thumb2Assembler::BindLiterals() {
// We don't add the padding here, that's done only after adjusting the Fixup sizes.
uint32_t code_size = buffer_.Size();
for (Literal& lit : literals_) {
@@ -100,6 +100,15 @@ void Thumb2Assembler::BindLiterals() {
BindLabel(label, code_size);
code_size += lit.GetSize();
}
+ return code_size;
+}
+
+void Thumb2Assembler::BindJumpTables(uint32_t code_size) {
+ for (JumpTable& table : jump_tables_) {
+ Label* label = table.GetLabel();
+ BindLabel(label, code_size);
+ code_size += table.GetSize();
+ }
}
void Thumb2Assembler::AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size,
@@ -144,7 +153,7 @@ uint32_t Thumb2Assembler::AdjustFixups() {
AdjustFixupIfNeeded(fixup, &current_code_size, &fixups_to_recalculate);
} while (!fixups_to_recalculate.empty());
- if ((current_code_size & 2) != 0 && !literals_.empty()) {
+ if ((current_code_size & 2) != 0 && (!literals_.empty() || !jump_tables_.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.
@@ -173,6 +182,13 @@ uint32_t Thumb2Assembler::AdjustFixups() {
label->Reinitialize();
label->BindTo(old_position + literals_adjustment);
}
+ for (JumpTable& table : jump_tables_) {
+ Label* label = table.GetLabel();
+ DCHECK(label->IsBound());
+ int old_position = label->Position();
+ label->Reinitialize();
+ label->BindTo(old_position + literals_adjustment);
+ }
}
return current_code_size;
@@ -229,6 +245,43 @@ void Thumb2Assembler::EmitLiterals() {
}
}
+void Thumb2Assembler::EmitJumpTables() {
+ if (!jump_tables_.empty()) {
+ // Jump tables require 4 byte alignment. (We don't support byte and half-word jump tables.)
+ uint32_t code_size = buffer_.Size();
+ DCHECK_ALIGNED(code_size, 2);
+ if ((code_size & 2u) != 0u) {
+ Emit16(0);
+ }
+ for (JumpTable& table : jump_tables_) {
+ // Bulk ensure capacity, as this may be large.
+ size_t orig_size = buffer_.Size();
+ buffer_.ExtendCapacity(orig_size + table.GetSize());
+#ifndef NDEBUG
+ buffer_.has_ensured_capacity_ = true;
+#endif
+
+ DCHECK_EQ(static_cast<size_t>(table.GetLabel()->Position()), buffer_.Size());
+ int32_t anchor_position = table.GetAnchorLabel()->Position() + 4;
+
+ for (Label* target : table.GetData()) {
+ // Ensure that the label was tracked, so that it will have the right position.
+ DCHECK(std::find(tracked_labels_.begin(), tracked_labels_.end(), target) !=
+ tracked_labels_.end());
+
+ int32_t offset = target->Position() - anchor_position;
+ buffer_.Emit<int32_t>(offset);
+ }
+
+#ifndef NDEBUG
+ buffer_.has_ensured_capacity_ = false;
+#endif
+ size_t new_size = buffer_.Size();
+ DCHECK_LE(new_size - orig_size, table.GetSize());
+ }
+ }
+}
+
inline int16_t Thumb2Assembler::BEncoding16(int32_t offset, Condition cond) {
DCHECK_ALIGNED(offset, 2);
int16_t encoding = B15 | B14;
@@ -382,12 +435,34 @@ inline int32_t Thumb2Assembler::LdrRtRnImm12Encoding(Register rt, Register rn, i
return B31 | B30 | B29 | B28 | B27 | B23 | B22 | B20 | (rn << 16) | (rt << 12) | offset;
}
+inline int16_t Thumb2Assembler::AdrEncoding16(Register rd, int32_t offset) {
+ DCHECK(IsUint<10>(offset));
+ DCHECK(IsAligned<4>(offset));
+ DCHECK(!IsHighRegister(rd));
+ return B15 | B13 | (rd << 8) | (offset >> 2);
+}
+
+inline int32_t Thumb2Assembler::AdrEncoding32(Register rd, int32_t offset) {
+ DCHECK(IsUint<12>(offset));
+ // Bit 26: offset[11]
+ // Bits 14-12: offset[10-8]
+ // Bits 7-0: offset[7-0]
+ int32_t immediate_mask =
+ ((offset & (1 << 11)) << (26 - 11)) |
+ ((offset & (7 << 8)) << (12 - 8)) |
+ (offset & 0xFF);
+ return B31 | B30 | B29 | B28 | B25 | B19 | B18 | B17 | B16 | (rd << 8) | immediate_mask;
+}
+
void Thumb2Assembler::FinalizeCode() {
ArmAssembler::FinalizeCode();
- BindLiterals();
+ uint32_t size_after_literals = BindLiterals();
+ BindJumpTables(size_after_literals);
uint32_t adjusted_code_size = AdjustFixups();
EmitFixups(adjusted_code_size);
EmitLiterals();
+ FinalizeTrackedLabels();
+ EmitJumpTables();
}
bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) {
@@ -1770,6 +1845,15 @@ inline size_t Thumb2Assembler::Fixup::SizeInBytes(Size size) {
case kLiteralFar:
return 14u;
+ case kLiteralAddr1KiB:
+ return 2u;
+ case kLiteralAddr4KiB:
+ return 4u;
+ case kLiteralAddr64KiB:
+ return 6u;
+ case kLiteralAddrFar:
+ return 10u;
+
case kLongOrFPLiteral1KiB:
return 4u;
case kLongOrFPLiteral256KiB:
@@ -1831,6 +1915,8 @@ inline int32_t Thumb2Assembler::Fixup::GetOffset(uint32_t current_code_size) con
case kLiteral1KiB:
case kLiteral4KiB:
case kLongOrFPLiteral1KiB:
+ case kLiteralAddr1KiB:
+ case kLiteralAddr4KiB:
DCHECK(diff >= 0 || (GetSize() == kLiteral1KiB && diff == -2));
diff += LiteralPoolPaddingSize(current_code_size);
// Load literal instructions round down the PC+4 to a multiple of 4, so if the PC
@@ -1843,12 +1929,14 @@ inline int32_t Thumb2Assembler::Fixup::GetOffset(uint32_t current_code_size) con
case kLiteral1MiB:
case kLiteral64KiB:
case kLongOrFPLiteral256KiB:
+ case kLiteralAddr64KiB:
DCHECK_GE(diff, 4); // The target must be at least 4 bytes after the ADD rX, PC.
diff -= 4; // One extra 32-bit MOV.
diff += LiteralPoolPaddingSize(current_code_size);
break;
case kLiteralFar:
case kLongOrFPLiteralFar:
+ case kLiteralAddrFar:
DCHECK_GE(diff, 8); // The target must be at least 4 bytes after the ADD rX, PC.
diff -= 8; // Extra MOVW+MOVT; both 32-bit.
diff += LiteralPoolPaddingSize(current_code_size);
@@ -1929,6 +2017,29 @@ uint32_t Thumb2Assembler::Fixup::AdjustSizeIfNeeded(uint32_t current_code_size)
// This encoding can reach any target.
break;
+ case kLiteralAddr1KiB:
+ DCHECK(!IsHighRegister(rn_));
+ if (IsUint<10>(GetOffset(current_code_size))) {
+ break;
+ }
+ current_code_size += IncreaseSize(kLiteralAddr4KiB);
+ FALLTHROUGH_INTENDED;
+ case kLiteralAddr4KiB:
+ if (IsUint<12>(GetOffset(current_code_size))) {
+ break;
+ }
+ current_code_size += IncreaseSize(kLiteralAddr64KiB);
+ FALLTHROUGH_INTENDED;
+ case kLiteralAddr64KiB:
+ if (IsUint<16>(GetOffset(current_code_size))) {
+ break;
+ }
+ current_code_size += IncreaseSize(kLiteralAddrFar);
+ FALLTHROUGH_INTENDED;
+ case kLiteralAddrFar:
+ // This encoding can reach any target.
+ break;
+
case kLongOrFPLiteral1KiB:
if (IsUint<10>(GetOffset(current_code_size))) {
break;
@@ -2055,6 +2166,42 @@ void Thumb2Assembler::Fixup::Emit(AssemblerBuffer* buffer, uint32_t code_size) c
break;
}
+ case kLiteralAddr1KiB: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int16_t encoding = AdrEncoding16(rn_, GetOffset(code_size));
+ buffer->Store<int16_t>(location_, encoding);
+ break;
+ }
+ case kLiteralAddr4KiB: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int32_t encoding = AdrEncoding32(rn_, GetOffset(code_size));
+ buffer->Store<int16_t>(location_, encoding >> 16);
+ buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(encoding & 0xffff));
+ break;
+ }
+ case kLiteralAddr64KiB: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int32_t mov_encoding = MovwEncoding32(rn_, GetOffset(code_size));
+ int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
+ buffer->Store<int16_t>(location_, mov_encoding >> 16);
+ buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(mov_encoding & 0xffff));
+ buffer->Store<int16_t>(location_ + 4u, add_pc_encoding);
+ break;
+ }
+ case kLiteralAddrFar: {
+ DCHECK(type_ == kLoadLiteralAddr);
+ int32_t offset = GetOffset(code_size);
+ int32_t movw_encoding = MovwEncoding32(rn_, offset & 0xffff);
+ int32_t movt_encoding = MovtEncoding32(rn_, offset & ~0xffff);
+ int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC);
+ buffer->Store<int16_t>(location_, movw_encoding >> 16);
+ buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(movw_encoding & 0xffff));
+ buffer->Store<int16_t>(location_ + 4u, movt_encoding >> 16);
+ buffer->Store<int16_t>(location_ + 6u, static_cast<int16_t>(movt_encoding & 0xffff));
+ buffer->Store<int16_t>(location_ + 8u, add_pc_encoding);
+ break;
+ }
+
case kLongOrFPLiteral1KiB: {
int32_t encoding = LoadWideOrFpEncoding(PC, GetOffset(code_size)); // DCHECKs type_.
buffer->Store<int16_t>(location_, encoding >> 16);
@@ -3260,6 +3407,25 @@ void Thumb2Assembler::AddConstant(Register rd, Register rn, int32_t value,
}
}
+void Thumb2Assembler::CmpConstant(Register rn, int32_t value, Condition cond) {
+ // We prefer to select the shorter code sequence rather than selecting add for
+ // positive values and sub for negatives ones, which would slightly improve
+ // the readability of generated code for some constants.
+ ShifterOperand shifter_op;
+ if (ShifterOperandCanHold(kNoRegister, rn, CMP, value, &shifter_op)) {
+ cmp(rn, shifter_op, cond);
+ } else if (ShifterOperandCanHold(kNoRegister, rn, CMN, ~value, &shifter_op)) {
+ cmn(rn, shifter_op, cond);
+ } else {
+ CHECK(rn != IP);
+ movw(IP, Low16Bits(value), cond);
+ uint16_t value_high = High16Bits(value);
+ if (value_high != 0) {
+ movt(IP, value_high, cond);
+ }
+ cmp(rn, ShifterOperand(IP), cond);
+ }
+}
void Thumb2Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) {
ShifterOperand shifter_op;
@@ -3476,5 +3642,39 @@ void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
b(label, NE);
}
}
+
+JumpTable* Thumb2Assembler::CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) {
+ jump_tables_.emplace_back(std::move(labels));
+ JumpTable* table = &jump_tables_.back();
+ DCHECK(!table->GetLabel()->IsBound());
+
+ bool use32bit = IsForced32Bit() || IsHighRegister(base_reg);
+ uint32_t location = buffer_.Size();
+ Fixup::Size size = use32bit ? Fixup::kLiteralAddr4KiB : Fixup::kLiteralAddr1KiB;
+ FixupId fixup_id = AddFixup(Fixup::LoadLiteralAddress(location, base_reg, size));
+ Emit16(static_cast<uint16_t>(table->GetLabel()->position_));
+ table->GetLabel()->LinkTo(fixup_id);
+ if (use32bit) {
+ Emit16(0);
+ }
+ DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size());
+
+ return table;
+}
+
+void Thumb2Assembler::EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) {
+ CHECK(!IsForced32Bit()) << "Forced 32-bit dispatch not implemented yet";
+ // 32-bit ADD doesn't support PC as an input, so we need a two-instruction sequence:
+ // SUB ip, ip, #0
+ // ADD pc, ip, reg
+ // TODO: Implement.
+
+ // The anchor's position needs to be fixed up before we can compute offsets - so make it a tracked
+ // label.
+ BindTrackedLabel(jump_table->GetAnchorLabel());
+
+ add(PC, PC, ShifterOperand(displacement_reg));
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 055b1379ad..38fd244087 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_UTILS_ARM_ASSEMBLER_THUMB2_H_
#include <deque>
+#include <utility>
#include <vector>
#include "base/logging.h"
@@ -304,6 +305,8 @@ class Thumb2Assembler FINAL : public ArmAssembler {
void AddConstant(Register rd, Register rn, int32_t value,
Condition cond = AL, SetCc set_cc = kCcDontCare) OVERRIDE;
+ void CmpConstant(Register rn, int32_t value, Condition cond = AL) OVERRIDE;
+
// Load and Store. May clobber IP.
void LoadImmediate(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
void MarkExceptionHandler(Label* label) OVERRIDE;
@@ -358,6 +361,12 @@ class Thumb2Assembler FINAL : public ArmAssembler {
force_32bit_ = true;
}
+ // Emit an ADR (or a sequence of instructions) to load the jump table address into base_reg. This
+ // will generate a fixup.
+ JumpTable* CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) OVERRIDE;
+ // Emit an ADD PC, X to dispatch a jump-table jump. This will generate a fixup.
+ void EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) OVERRIDE;
+
private:
typedef uint16_t FixupId;
@@ -399,6 +408,7 @@ class Thumb2Assembler FINAL : public ArmAssembler {
kCompareAndBranchXZero, // cbz/cbnz.
kLoadLiteralNarrow, // Load narrrow integer literal.
kLoadLiteralWide, // Load wide integer literal.
+ kLoadLiteralAddr, // Load address of literal (used for jump table).
kLoadFPLiteralSingle, // Load FP literal single.
kLoadFPLiteralDouble, // Load FP literal double.
};
@@ -429,6 +439,16 @@ class Thumb2Assembler FINAL : public ArmAssembler {
// MOV rX, imm16 + MOVT rX, imm16 + ADD rX, pc + LDR rX, [rX]; any offset; 14 bytes.
kLiteralFar,
+ // Load literal base addr.
+ // ADR rX, label; X < 8; 8 bit immediate, shifted to 10 bit. 2 bytes.
+ kLiteralAddr1KiB,
+ // ADR rX, label; 4KiB offset. 4 bytes.
+ kLiteralAddr4KiB,
+ // MOV rX, imm16 + ADD rX, pc; 64KiB offset. 6 bytes.
+ kLiteralAddr64KiB,
+ // MOV rX, imm16 + MOVT rX, imm16 + ADD rX, pc; any offset; 10 bytes.
+ kLiteralAddrFar,
+
// Load long or FP literal variants.
// VLDR s/dX, label; 32-bit insn, up to 1KiB offset; 4 bytes.
kLongOrFPLiteral1KiB,
@@ -457,7 +477,7 @@ class Thumb2Assembler FINAL : public ArmAssembler {
}
// Load narrow literal.
- static Fixup LoadNarrowLiteral(uint32_t location, Register rt, Size size = kLiteral1KiB) {
+ static Fixup LoadNarrowLiteral(uint32_t location, Register rt, Size size) {
DCHECK(size == kLiteral1KiB || size == kLiteral4KiB || size == kLiteral64KiB ||
size == kLiteral1MiB || size == kLiteralFar);
DCHECK(!IsHighRegister(rt) || (size != kLiteral1KiB && size != kLiteral64KiB));
@@ -493,6 +513,14 @@ class Thumb2Assembler FINAL : public ArmAssembler {
AL, kLoadFPLiteralDouble, size, location);
}
+ static Fixup LoadLiteralAddress(uint32_t location, Register rt, Size size) {
+ DCHECK(size == kLiteralAddr1KiB || size == kLiteralAddr4KiB || size == kLiteralAddr64KiB ||
+ size == kLiteralAddrFar);
+ DCHECK(!IsHighRegister(rt) || size != kLiteralAddr1KiB);
+ return Fixup(rt, kNoRegister, kNoSRegister, kNoDRegister,
+ AL, kLoadLiteralAddr, size, location);
+ }
+
Type GetType() const {
return type_;
}
@@ -756,12 +784,14 @@ class Thumb2Assembler FINAL : public ArmAssembler {
}
void BindLabel(Label* label, uint32_t bound_pc);
- void BindLiterals();
+ uint32_t BindLiterals();
+ void BindJumpTables(uint32_t code_size);
void AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size,
std::deque<FixupId>* fixups_to_recalculate);
uint32_t AdjustFixups();
void EmitFixups(uint32_t adjusted_code_size);
void EmitLiterals();
+ void EmitJumpTables();
static int16_t BEncoding16(int32_t offset, Condition cond);
static int32_t BEncoding32(int32_t offset, Condition cond);
@@ -778,6 +808,8 @@ class Thumb2Assembler FINAL : public ArmAssembler {
static int32_t VldrdEncoding32(DRegister dd, Register rn, int32_t offset);
static int16_t LdrRtRnImm5Encoding16(Register rt, Register rn, int32_t offset);
static int32_t LdrRtRnImm12Encoding(Register rt, Register rn, int32_t offset);
+ static int16_t AdrEncoding16(Register rd, int32_t offset);
+ static int32_t AdrEncoding32(Register rd, int32_t offset);
std::vector<Fixup> fixups_;
std::unique_ptr<FixupId[]> fixup_dependents_;
@@ -786,6 +818,9 @@ class Thumb2Assembler FINAL : public ArmAssembler {
// without invalidating pointers and references to existing elements.
std::deque<Literal> literals_;
+ // Jump table list.
+ std::deque<JumpTable> jump_tables_;
+
// Data for AdjustedPosition(), see the description there.
uint32_t last_position_adjustment_;
uint32_t last_old_position_;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 9c08ce017e..cb4b20b5ba 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -17,6 +17,7 @@
#include "assembler_thumb2.h"
#include "base/stl_util.h"
+#include "base/stringprintf.h"
#include "utils/assembler_test.h"
namespace art {
@@ -1011,6 +1012,315 @@ TEST_F(AssemblerThumb2Test, LoadLiteralBeyondMax1KiBDueToAlignmentOnSecondPass)
__ GetAdjustedPosition(label.Position()));
}
+TEST_F(AssemblerThumb2Test, BindTrackedLabel) {
+ Label non_tracked, tracked, branch_target;
+
+ // A few dummy loads on entry.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // A branch that will need to be fixed up.
+ __ cbz(arm::R0, &branch_target);
+
+ // Some more dummy loads.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Now insert tracked and untracked label.
+ __ Bind(&non_tracked);
+ __ BindTrackedLabel(&tracked);
+
+ // A lot of dummy loads, to ensure the branch needs resizing.
+ constexpr size_t kLdrR0R0CountLong = 60;
+ for (size_t i = 0; i != kLdrR0R0CountLong; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Bind the branch target.
+ __ Bind(&branch_target);
+
+ // One more load.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ "cmp r0, #0\n" // cbz r0, 1f
+ "beq.n 1f\n" +
+ RepeatInsn(kLdrR0R0Count + kLdrR0R0CountLong, "ldr r0, [r0]\n") +
+ "1:\n"
+ "ldr r0, [r0]\n";
+ DriverStr(expected, "BindTrackedLabel");
+
+ // Expectation is that the tracked label should have moved.
+ EXPECT_LT(non_tracked.Position(), tracked.Position());
+}
+
+TEST_F(AssemblerThumb2Test, JumpTable) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) { // Note: odd so there's no alignment
+ __ ldr(arm::R0, arm::Address(arm::R0)); // necessary, as gcc as emits nops,
+ } // whereas we emit 0 != nop.
+
+ static_assert((kLdrR0R0Count + 3) * 2 < 1 * KB, "Too much offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ "adr r1, .Ljump_table\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTable");
+}
+
+// Test for >1K fixup.
+TEST_F(AssemblerThumb2Test, JumpTable4K) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ constexpr size_t kLdrR0R0Count2 = 600; // Note: even so there's no alignment
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
+ __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
+ }
+
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 1 * KB, "Not enough offset");
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 4 * KB, "Too much offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ "adr r1, .Ljump_table\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTable4K");
+}
+
+// Test for >4K fixup.
+TEST_F(AssemblerThumb2Test, JumpTable64K) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ constexpr size_t kLdrR0R0Count2 = 2601; // Note: odd so there's no alignment
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
+ __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
+ }
+
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 4 * KB, "Not enough offset");
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 < 64 * KB, "Too much offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
+ // (Note: have to use constants, as labels aren't accepted.
+ "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
+ ") * 2 - 4) & 0xFFFF)\n"
+ "add r1, pc\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTable64K");
+}
+
+// Test for >64K fixup.
+TEST_F(AssemblerThumb2Test, JumpTableFar) {
+ // The jump table. Use three labels.
+ Label label1, label2, label3;
+ std::vector<Label*> labels({ &label1, &label2, &label3 });
+
+ // A few dummy loads on entry, interspersed with 2 labels.
+ constexpr size_t kLdrR0R0Count = 5;
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label1);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label2);
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+
+ // Create the jump table, emit the base load.
+ arm::JumpTable* jump_table = __ CreateJumpTable(std::move(labels), arm::R1);
+
+ // Dummy computation, stand-in for the address. We're only testing the jump table here, not how
+ // it's being used.
+ __ ldr(arm::R0, arm::Address(arm::R0));
+
+ // Emit the jump
+ __ EmitJumpTableDispatch(jump_table, arm::R1);
+
+ // Some more dummy instructions.
+ for (size_t i = 0; i != kLdrR0R0Count; ++i) {
+ __ ldr(arm::R0, arm::Address(arm::R0));
+ }
+ __ BindTrackedLabel(&label3);
+ constexpr size_t kLdrR0R0Count2 = 70001; // Note: odd so there's no alignment
+ for (size_t i = 0; i != kLdrR0R0Count2; ++i) { // necessary, as gcc as emits nops,
+ __ ldr(arm::R0, arm::Address(arm::R0)); // whereas we emit 0 != nop.
+ }
+
+ static_assert((kLdrR0R0Count + kLdrR0R0Count2 + 3) * 2 > 64 * KB, "Not enough offset");
+
+ std::string expected =
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L1:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L2:\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ // ~ adr r1, .Ljump_table, gcc as can't seem to fix up a large offset itself.
+ // (Note: have to use constants, as labels aren't accepted.
+ "movw r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
+ ") * 2 - 4) & 0xFFFF)\n"
+ "movt r1, #(((3 + " + StringPrintf("%zu", kLdrR0R0Count + kLdrR0R0Count2) +
+ ") * 2 - 4) >> 16)\n"
+ ".Lhelp:"
+ "add r1, pc\n"
+ "ldr r0, [r0]\n"
+ ".Lbase:\n"
+ "add pc, r1\n" +
+ RepeatInsn(kLdrR0R0Count, "ldr r0, [r0]\n") +
+ ".L3:\n" +
+ RepeatInsn(kLdrR0R0Count2, "ldr r0, [r0]\n") +
+ ".align 2\n"
+ ".Ljump_table:\n"
+ ".4byte (.L1 - .Lbase - 4)\n"
+ ".4byte (.L2 - .Lbase - 4)\n"
+ ".4byte (.L3 - .Lbase - 4)\n";
+ DriverStr(expected, "JumpTableFar");
+}
+
TEST_F(AssemblerThumb2Test, Clz) {
__ clz(arm::R0, arm::R1);
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index d97a2a40b2..dfe6babb25 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -227,6 +227,8 @@ class AssemblerBuffer {
// Returns the position in the instruction stream.
int GetPosition() { return cursor_ - contents_; }
+ void ExtendCapacity(size_t min_capacity = 0u);
+
private:
// The limit is set to kMinimumGap bytes before the end of the data area.
// This leaves enough space for the longest possible instruction and allows
@@ -261,8 +263,6 @@ class AssemblerBuffer {
return data + capacity - kMinimumGap;
}
- void ExtendCapacity(size_t min_capacity = 0u);
-
friend class AssemblerFixup;
};
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
new file mode 100644
index 0000000000..ac5481336b
--- /dev/null
+++ b/compiler/utils/dedupe_set-inl.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_DEDUPE_SET_INL_H_
+#define ART_COMPILER_UTILS_DEDUPE_SET_INL_H_
+
+#include "dedupe_set.h"
+
+#include <algorithm>
+#include <inttypes.h>
+#include <unordered_map>
+
+#include "base/mutex.h"
+#include "base/hash_set.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "base/time_utils.h"
+
+namespace art {
+
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
+ HashType kShard>
+struct DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Stats {
+ size_t collision_sum = 0u;
+ size_t collision_max = 0u;
+ size_t total_probe_distance = 0u;
+ size_t total_size = 0u;
+};
+
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
+ HashType kShard>
+class DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Shard {
+ public:
+ Shard(const Alloc& alloc, const std::string& lock_name)
+ : alloc_(alloc),
+ lock_name_(lock_name),
+ lock_(lock_name_.c_str()),
+ keys_() {
+ }
+
+ ~Shard() {
+ for (const HashedKey<StoreKey>& key : keys_) {
+ DCHECK(key.Key() != nullptr);
+ alloc_.Destroy(key.Key());
+ }
+ }
+
+ const StoreKey* Add(Thread* self, size_t hash, const InKey& in_key) REQUIRES(!lock_) {
+ MutexLock lock(self, lock_);
+ HashedKey<InKey> hashed_in_key(hash, &in_key);
+ auto it = keys_.Find(hashed_in_key);
+ if (it != keys_.end()) {
+ DCHECK(it->Key() != nullptr);
+ return it->Key();
+ }
+ const StoreKey* store_key = alloc_.Copy(in_key);
+ keys_.Insert(HashedKey<StoreKey> { hash, store_key });
+ return store_key;
+ }
+
+ void UpdateStats(Thread* self, Stats* global_stats) REQUIRES(!lock_) {
+ // HashSet<> doesn't keep entries ordered by hash, so we actually allocate memory
+ // for bookkeeping while collecting the stats.
+ std::unordered_map<HashType, size_t> stats;
+ {
+ MutexLock lock(self, lock_);
+ // Note: The total_probe_distance will be updated with the current state.
+ // It may have been higher before a re-hash.
+ global_stats->total_probe_distance += keys_.TotalProbeDistance();
+ global_stats->total_size += keys_.Size();
+ for (const HashedKey<StoreKey>& key : keys_) {
+ auto it = stats.find(key.Hash());
+ if (it == stats.end()) {
+ stats.insert({key.Hash(), 1u});
+ } else {
+ ++it->second;
+ }
+ }
+ }
+ for (const auto& entry : stats) {
+ size_t number_of_entries = entry.second;
+ if (number_of_entries > 1u) {
+ global_stats->collision_sum += number_of_entries - 1u;
+ global_stats->collision_max = std::max(global_stats->collision_max, number_of_entries);
+ }
+ }
+ }
+
+ private:
+ template <typename T>
+ class HashedKey {
+ public:
+ HashedKey() : hash_(0u), key_(nullptr) { }
+ HashedKey(size_t hash, const T* key) : hash_(hash), key_(key) { }
+
+ size_t Hash() const {
+ return hash_;
+ }
+
+ const T* Key() const {
+ return key_;
+ }
+
+ bool IsEmpty() const {
+ return Key() == nullptr;
+ }
+
+ void MakeEmpty() {
+ key_ = nullptr;
+ }
+
+ private:
+ size_t hash_;
+ const T* key_;
+ };
+
+ class ShardEmptyFn {
+ public:
+ bool IsEmpty(const HashedKey<StoreKey>& key) const {
+ return key.IsEmpty();
+ }
+
+ void MakeEmpty(HashedKey<StoreKey>& key) {
+ key.MakeEmpty();
+ }
+ };
+
+ struct ShardHashFn {
+ template <typename T>
+ size_t operator()(const HashedKey<T>& key) const {
+ return key.Hash();
+ }
+ };
+
+ struct ShardPred {
+ typename std::enable_if<!std::is_same<StoreKey, InKey>::value, bool>::type
+ operator()(const HashedKey<StoreKey>& lhs, const HashedKey<StoreKey>& rhs) const {
+ DCHECK(lhs.Key() != nullptr);
+ DCHECK(rhs.Key() != nullptr);
+ // Rehashing: stored keys are already deduplicated, so we can simply compare key pointers.
+ return lhs.Key() == rhs.Key();
+ }
+
+ template <typename LeftT, typename RightT>
+ bool operator()(const HashedKey<LeftT>& lhs, const HashedKey<RightT>& rhs) const {
+ DCHECK(lhs.Key() != nullptr);
+ DCHECK(rhs.Key() != nullptr);
+ return lhs.Hash() == rhs.Hash() &&
+ lhs.Key()->size() == rhs.Key()->size() &&
+ std::equal(lhs.Key()->begin(), lhs.Key()->end(), rhs.Key()->begin());
+ }
+ };
+
+ Alloc alloc_;
+ const std::string lock_name_;
+ Mutex lock_;
+ HashSet<HashedKey<StoreKey>, ShardEmptyFn, ShardHashFn, ShardPred> keys_ GUARDED_BY(lock_);
+};
+
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
+ HashType kShard>
+const StoreKey* DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Add(
+ Thread* self, const InKey& key) {
+ uint64_t hash_start;
+ if (kIsDebugBuild) {
+ hash_start = NanoTime();
+ }
+ HashType raw_hash = HashFunc()(key);
+ if (kIsDebugBuild) {
+ uint64_t hash_end = NanoTime();
+ hash_time_ += hash_end - hash_start;
+ }
+ HashType shard_hash = raw_hash / kShard;
+ HashType shard_bin = raw_hash % kShard;
+ return shards_[shard_bin]->Add(self, shard_hash, key);
+}
+
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
+ HashType kShard>
+DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DedupeSet(const char* set_name,
+ const Alloc& alloc)
+ : hash_time_(0) {
+ for (HashType i = 0; i < kShard; ++i) {
+ std::ostringstream oss;
+ oss << set_name << " lock " << i;
+ shards_[i].reset(new Shard(alloc, oss.str()));
+ }
+}
+
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
+ HashType kShard>
+DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::~DedupeSet() {
+ // Everything done by member destructors.
+}
+
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
+ HashType kShard>
+std::string DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DumpStats(
+ Thread* self) const {
+ Stats stats;
+ for (HashType shard = 0; shard < kShard; ++shard) {
+ shards_[shard]->UpdateStats(self, &stats);
+ }
+ return StringPrintf("%zu collisions, %zu max hash collisions, "
+ "%zu/%zu probe distance, %" PRIu64 " ns hash time",
+ stats.collision_sum,
+ stats.collision_max,
+ stats.total_probe_distance,
+ stats.total_size,
+ hash_time_);
+}
+
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_DEDUPE_SET_INL_H_
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index 2c4a689096..b62f216842 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -17,151 +17,41 @@
#ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_
#define ART_COMPILER_UTILS_DEDUPE_SET_H_
-#include <algorithm>
-#include <inttypes.h>
#include <memory>
-#include <set>
+#include <stdint.h>
#include <string>
-#include "base/mutex.h"
-#include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "base/time_utils.h"
-#include "utils/swap_space.h"
+#include "base/macros.h"
namespace art {
+class Thread;
+
// A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the
// Add method. The data-structure is thread-safe through the use of internal locks, it also
// supports the lock being sharded.
-template <typename InKey, typename StoreKey, typename HashType, typename HashFunc,
+template <typename InKey,
+ typename StoreKey,
+ typename Alloc,
+ typename HashType,
+ typename HashFunc,
HashType kShard = 1>
class DedupeSet {
- typedef std::pair<HashType, const InKey*> HashedInKey;
- struct HashedKey {
- StoreKey* store_ptr;
- union {
- HashType store_hash; // Valid if store_ptr != null.
- const HashedInKey* in_key; // Valid if store_ptr == null.
- };
- };
-
- class Comparator {
- public:
- bool operator()(const HashedKey& a, const HashedKey& b) const {
- HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first;
- HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first;
- if (a_hash != b_hash) {
- return a_hash < b_hash;
- }
- if (a.store_ptr != nullptr && b.store_ptr != nullptr) {
- return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
- b.store_ptr->begin(), b.store_ptr->end());
- } else if (a.store_ptr != nullptr && b.store_ptr == nullptr) {
- return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(),
- b.in_key->second->begin(), b.in_key->second->end());
- } else if (a.store_ptr == nullptr && b.store_ptr != nullptr) {
- return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
- b.store_ptr->begin(), b.store_ptr->end());
- } else {
- return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(),
- b.in_key->second->begin(), b.in_key->second->end());
- }
- }
- };
-
public:
- StoreKey* Add(Thread* self, const InKey& key) {
- uint64_t hash_start;
- if (kIsDebugBuild) {
- hash_start = NanoTime();
- }
- HashType raw_hash = HashFunc()(key);
- if (kIsDebugBuild) {
- uint64_t hash_end = NanoTime();
- hash_time_ += hash_end - hash_start;
- }
- HashType shard_hash = raw_hash / kShard;
- HashType shard_bin = raw_hash % kShard;
- HashedInKey hashed_in_key(shard_hash, &key);
- HashedKey hashed_key;
- hashed_key.store_ptr = nullptr;
- hashed_key.in_key = &hashed_in_key;
- MutexLock lock(self, *lock_[shard_bin]);
- auto it = keys_[shard_bin].find(hashed_key);
- if (it != keys_[shard_bin].end()) {
- DCHECK(it->store_ptr != nullptr);
- return it->store_ptr;
- }
- hashed_key.store_ptr = CreateStoreKey(key);
- hashed_key.store_hash = shard_hash;
- keys_[shard_bin].insert(hashed_key);
- return hashed_key.store_ptr;
- }
+ // Add a new key to the dedupe set if not present. Return the equivalent deduplicated stored key.
+ const StoreKey* Add(Thread* self, const InKey& key);
- DedupeSet(const char* set_name, SwapAllocator<void>& alloc)
- : allocator_(alloc), hash_time_(0) {
- for (HashType i = 0; i < kShard; ++i) {
- std::ostringstream oss;
- oss << set_name << " lock " << i;
- lock_name_[i] = oss.str();
- lock_[i].reset(new Mutex(lock_name_[i].c_str()));
- }
- }
+ DedupeSet(const char* set_name, const Alloc& alloc);
- ~DedupeSet() {
- // Have to manually free all pointers.
- for (auto& shard : keys_) {
- for (const auto& hashed_key : shard) {
- DCHECK(hashed_key.store_ptr != nullptr);
- DeleteStoreKey(hashed_key.store_ptr);
- }
- }
- }
+ ~DedupeSet();
- std::string DumpStats() const {
- size_t collision_sum = 0;
- size_t collision_max = 0;
- for (HashType shard = 0; shard < kShard; ++shard) {
- HashType last_hash = 0;
- size_t collision_cur_max = 0;
- for (const HashedKey& key : keys_[shard]) {
- DCHECK(key.store_ptr != nullptr);
- if (key.store_hash == last_hash) {
- collision_cur_max++;
- if (collision_cur_max > 1) {
- collision_sum++;
- if (collision_cur_max > collision_max) {
- collision_max = collision_cur_max;
- }
- }
- } else {
- collision_cur_max = 1;
- last_hash = key.store_hash;
- }
- }
- }
- return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time",
- collision_sum, collision_max, hash_time_);
- }
+ std::string DumpStats(Thread* self) const;
private:
- StoreKey* CreateStoreKey(const InKey& key) {
- StoreKey* ret = allocator_.allocate(1);
- allocator_.construct(ret, key.begin(), key.end(), allocator_);
- return ret;
- }
-
- void DeleteStoreKey(StoreKey* key) {
- SwapAllocator<StoreKey> alloc(allocator_);
- alloc.destroy(key);
- alloc.deallocate(key, 1);
- }
+ struct Stats;
+ class Shard;
- std::string lock_name_[kShard];
- std::unique_ptr<Mutex> lock_[kShard];
- std::set<HashedKey, Comparator> keys_[kShard];
- SwapAllocator<StoreKey> allocator_;
+ std::unique_ptr<Shard> shards_[kShard];
uint64_t hash_time_;
DISALLOW_COPY_AND_ASSIGN(DedupeSet);
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 637964e484..60a891d6a2 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -18,15 +18,18 @@
#include <algorithm>
#include <cstdio>
+#include <vector>
+#include "dedupe_set-inl.h"
#include "gtest/gtest.h"
#include "thread-inl.h"
+#include "utils/array_ref.h"
namespace art {
-class DedupeHashFunc {
+class DedupeSetTestHashFunc {
public:
- size_t operator()(const std::vector<uint8_t>& array) const {
+ size_t operator()(const ArrayRef<const uint8_t>& array) const {
size_t hash = 0;
for (uint8_t c : array) {
hash += c;
@@ -36,46 +39,52 @@ class DedupeHashFunc {
return hash;
}
};
+
+class DedupeSetTestAlloc {
+ public:
+ const std::vector<uint8_t>* Copy(const ArrayRef<const uint8_t>& src) {
+ return new std::vector<uint8_t>(src.begin(), src.end());
+ }
+
+ void Destroy(const std::vector<uint8_t>* key) {
+ delete key;
+ }
+};
+
TEST(DedupeSetTest, Test) {
Thread* self = Thread::Current();
- typedef std::vector<uint8_t> ByteArray;
- SwapAllocator<void> swap(nullptr);
- DedupeSet<ByteArray, SwapVector<uint8_t>, size_t, DedupeHashFunc> deduplicator("test", swap);
- SwapVector<uint8_t>* array1;
+ DedupeSetTestAlloc alloc;
+ DedupeSet<ArrayRef<const uint8_t>,
+ std::vector<uint8_t>,
+ DedupeSetTestAlloc,
+ size_t,
+ DedupeSetTestHashFunc> deduplicator("test", alloc);
+ const std::vector<uint8_t>* array1;
{
- ByteArray test1;
- test1.push_back(10);
- test1.push_back(20);
- test1.push_back(30);
- test1.push_back(45);
-
+ uint8_t raw_test1[] = { 10u, 20u, 30u, 45u };
+ ArrayRef<const uint8_t> test1(raw_test1);
array1 = deduplicator.Add(self, test1);
ASSERT_NE(array1, nullptr);
ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array1->begin()));
}
- SwapVector<uint8_t>* array2;
+ const std::vector<uint8_t>* array2;
{
- ByteArray test1;
- test1.push_back(10);
- test1.push_back(20);
- test1.push_back(30);
- test1.push_back(45);
- array2 = deduplicator.Add(self, test1);
+ uint8_t raw_test2[] = { 10u, 20u, 30u, 45u };
+ ArrayRef<const uint8_t> test2(raw_test2);
+ array2 = deduplicator.Add(self, test2);
ASSERT_EQ(array2, array1);
- ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array2->begin()));
+ ASSERT_TRUE(std::equal(test2.begin(), test2.end(), array2->begin()));
}
- SwapVector<uint8_t>* array3;
+ const std::vector<uint8_t>* array3;
{
- ByteArray test1;
- test1.push_back(10);
- test1.push_back(22);
- test1.push_back(30);
- test1.push_back(47);
- array3 = deduplicator.Add(self, test1);
+ uint8_t raw_test3[] = { 10u, 22u, 30u, 47u };
+ ArrayRef<const uint8_t> test3(raw_test3);
+ array3 = deduplicator.Add(self, test3);
ASSERT_NE(array3, nullptr);
- ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array3->begin()));
+ ASSERT_NE(array3, array1);
+ ASSERT_TRUE(std::equal(test3.begin(), test3.end(), array3->begin()));
}
}
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 5347bf0302..d6caa3c338 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -720,6 +720,14 @@ void X86Assembler::ucomiss(XmmRegister a, XmmRegister b) {
}
+void X86Assembler::ucomiss(XmmRegister a, const Address& b) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x0F);
+ EmitUint8(0x2E);
+ EmitOperand(a, b);
+}
+
+
void X86Assembler::ucomisd(XmmRegister a, XmmRegister b) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -729,6 +737,15 @@ void X86Assembler::ucomisd(XmmRegister a, XmmRegister b) {
}
+void X86Assembler::ucomisd(XmmRegister a, const Address& b) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x2E);
+ EmitOperand(a, b);
+}
+
+
void X86Assembler::roundsd(XmmRegister dst, XmmRegister src, const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index b50fda907a..655af9c184 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -417,7 +417,9 @@ class X86Assembler FINAL : public Assembler {
void comiss(XmmRegister a, XmmRegister b);
void comisd(XmmRegister a, XmmRegister b);
void ucomiss(XmmRegister a, XmmRegister b);
+ void ucomiss(XmmRegister a, const Address& b);
void ucomisd(XmmRegister a, XmmRegister b);
+ void ucomisd(XmmRegister a, const Address& b);
void roundsd(XmmRegister dst, XmmRegister src, const Immediate& imm);
void roundss(XmmRegister dst, XmmRegister src, const Immediate& imm);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 16f9db43fb..a9b991c7a0 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -306,6 +306,19 @@ TEST_F(AssemblerX86Test, RollImm) {
DriverStr(RepeatRI(&x86::X86Assembler::roll, 1U, "roll ${imm}, %{reg}"), "rolli");
}
+TEST_F(AssemblerX86Test, UComissAddr) {
+ GetAssembler()->ucomiss(x86::XmmRegister(x86::XMM0), x86::Address(x86::EAX, 0));
+ const char* expected = "ucomiss 0(%EAX), %xmm0\n";
+ DriverStr(expected, "ucomiss");
+}
+
+TEST_F(AssemblerX86Test, UComisdAddr) {
+ GetAssembler()->ucomisd(x86::XmmRegister(x86::XMM0), x86::Address(x86::EAX, 0));
+ const char* expected = "ucomisd 0(%EAX), %xmm0\n";
+ DriverStr(expected, "ucomisd");
+}
+
+
/////////////////
// Near labels //
/////////////////
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 384b8794c1..2653807369 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -126,11 +126,12 @@ static std::string StrippedCommandLine() {
// However, we prefer to drop this when we saw --zip-fd.
if (saw_zip_fd) {
- // Drop anything --zip-X, --dex-X, --oat-X, --swap-X.
+ // Drop anything --zip-X, --dex-X, --oat-X, --swap-X, or --app-image-X
if (StartsWith(original_argv[i], "--zip-") ||
StartsWith(original_argv[i], "--dex-") ||
StartsWith(original_argv[i], "--oat-") ||
- StartsWith(original_argv[i], "--swap-")) {
+ StartsWith(original_argv[i], "--swap-") ||
+ StartsWith(original_argv[i], "--app-image-")) {
continue;
}
}
@@ -336,6 +337,12 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" --swap-fd=<file-descriptor>: specifies a file to use for swap (by descriptor).");
UsageError(" Example: --swap-fd=10");
UsageError("");
+ UsageError(" --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
+ UsageError(" Example: --app-image-fd=10");
+ UsageError("");
+ UsageError(" --app-image-file=<file-name>: specify a file name for app image.");
+ UsageError(" Example: --app-image-file=/data/dalvik-cache/system@app@Calculator.apk.art");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -445,38 +452,6 @@ class WatchDog {
pthread_t pthread_;
};
-static void ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
- std::string::size_type colon = s.find(c);
- if (colon == std::string::npos) {
- Usage("Missing char %c in option %s\n", c, s.c_str());
- }
- // Add one to remove the char we were trimming until.
- *parsed_value = s.substr(colon + 1);
-}
-
-static void ParseDouble(const std::string& option, char after_char, double min, double max,
- double* parsed_value) {
- std::string substring;
- ParseStringAfterChar(option, after_char, &substring);
- bool sane_val = true;
- double value;
- if (false) {
- // TODO: this doesn't seem to work on the emulator. b/15114595
- std::stringstream iss(substring);
- iss >> value;
- // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
- sane_val = iss.eof() && (value >= min) && (value <= max);
- } else {
- char* end = nullptr;
- value = strtod(substring.c_str(), &end);
- sane_val = *end == '\0' && value >= min && value <= max;
- }
- if (!sane_val) {
- Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
- }
- *parsed_value = value;
-}
-
static constexpr size_t kMinDexFilesForSwap = 2;
static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
@@ -516,7 +491,8 @@ class Dex2Oat FINAL {
compiled_classes_filename_(nullptr),
compiled_methods_zip_filename_(nullptr),
compiled_methods_filename_(nullptr),
- image_(false),
+ app_image_(false),
+ boot_image_(false),
is_host_(false),
driver_(nullptr),
dump_stats_(false),
@@ -525,6 +501,7 @@ class Dex2Oat FINAL {
dump_slow_timing_(kIsDebugBuild),
dump_cfg_append_(false),
swap_fd_(-1),
+ app_image_fd_(kInvalidImageFd),
timings_(timings) {}
~Dex2Oat() {
@@ -555,66 +532,21 @@ class Dex2Oat FINAL {
struct ParserOptions {
std::string oat_symbols;
std::string boot_image_filename;
- const char* compiler_filter_string = nullptr;
- CompilerOptions::CompilerFilter compiler_filter = CompilerOptions::kDefaultCompilerFilter;
- bool compile_pic = false;
- int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold;
- int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold;
- int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold;
- int tiny_method_threshold = CompilerOptions::kDefaultTinyMethodThreshold;
- int num_dex_methods_threshold = CompilerOptions::kDefaultNumDexMethodsThreshold;
- static constexpr int kUnsetInlineDepthLimit = -1;
- int inline_depth_limit = kUnsetInlineDepthLimit;
- static constexpr int kUnsetInlineMaxCodeUnits = -1;
- int inline_max_code_units = kUnsetInlineMaxCodeUnits;
-
- // Profile file to use
- double top_k_profile_threshold = CompilerOptions::kDefaultTopKProfileThreshold;
-
- bool debuggable = false;
- bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
- bool generate_debug_info = kIsDebugBuild;
bool watch_dog_enabled = true;
- bool abort_on_hard_verifier_error = false;
bool requested_specific_compiler = false;
-
- bool implicit_null_checks = false;
- bool implicit_so_checks = false;
- bool implicit_suspend_checks = false;
-
- PassManagerOptions pass_manager_options;
-
std::string error_msg;
};
- template <typename T>
- static void ParseUintOption(const StringPiece& option,
- const std::string& option_name,
- T* out,
- bool is_long_option = true) {
- std::string option_prefix = option_name + (is_long_option ? "=" : "");
- DCHECK(option.starts_with(option_prefix));
- const char* value_string = option.substr(option_prefix.size()).data();
- int64_t parsed_integer_value;
- if (!ParseInt(value_string, &parsed_integer_value)) {
- Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
- }
- if (parsed_integer_value < 0) {
- Usage("%s passed a negative value %d", option_name.c_str(), parsed_integer_value);
- }
- *out = dchecked_integral_cast<T>(parsed_integer_value);
- }
-
void ParseZipFd(const StringPiece& option) {
- ParseUintOption(option, "--zip-fd", &zip_fd_);
+ ParseUintOption(option, "--zip-fd", &zip_fd_, Usage);
}
void ParseOatFd(const StringPiece& option) {
- ParseUintOption(option, "--oat-fd", &oat_fd_);
+ ParseUintOption(option, "--oat-fd", &oat_fd_, Usage);
}
void ParseJ(const StringPiece& option) {
- ParseUintOption(option, "-j", &thread_count_, /* is_long_option */ false);
+ ParseUintOption(option, "-j", &thread_count_, Usage, /* is_long_option */ false);
}
void ParseBase(const StringPiece& option) {
@@ -685,80 +617,17 @@ class Dex2Oat FINAL {
}
}
- void ParseHugeMethodMax(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--huge-method-max", &parser_options->huge_method_threshold);
- }
-
- void ParseLargeMethodMax(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--large-method-max", &parser_options->large_method_threshold);
- }
-
- void ParseSmallMethodMax(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--small-method-max", &parser_options->small_method_threshold);
- }
-
- void ParseTinyMethodMax(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--tiny-method-max", &parser_options->tiny_method_threshold);
- }
-
- void ParseNumDexMethods(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--num-dex-methods", &parser_options->num_dex_methods_threshold);
- }
-
- void ParseInlineDepthLimit(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--inline-depth-limit", &parser_options->inline_depth_limit);
- }
-
- void ParseInlineMaxCodeUnits(const StringPiece& option, ParserOptions* parser_options) {
- ParseUintOption(option, "--inline-max-code-units=", &parser_options->inline_max_code_units);
- }
-
- void ParseDisablePasses(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--disable-passes="));
- const std::string disable_passes = option.substr(strlen("--disable-passes=")).data();
- parser_options->pass_manager_options.SetDisablePassList(disable_passes);
- }
-
- void ParsePrintPasses(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--print-passes="));
- const std::string print_passes = option.substr(strlen("--print-passes=")).data();
- parser_options->pass_manager_options.SetPrintPassList(print_passes);
- }
-
- void ParseDumpCfgPasses(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--dump-cfg-passes="));
- const std::string dump_passes_string = option.substr(strlen("--dump-cfg-passes=")).data();
- parser_options->pass_manager_options.SetDumpPassList(dump_passes_string);
- }
-
- void ParsePassOptions(const StringPiece& option, ParserOptions* parser_options) {
- DCHECK(option.starts_with("--pass-options="));
- const std::string pass_options = option.substr(strlen("--pass-options=")).data();
- parser_options->pass_manager_options.SetOverriddenPassOptions(pass_options);
- }
+ void ProcessOptions(ParserOptions* parser_options) {
+ boot_image_ = !image_filename_.empty();
+ app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
- void ParseDumpInitFailures(const StringPiece& option) {
- DCHECK(option.starts_with("--dump-init-failures="));
- std::string file_name = option.substr(strlen("--dump-init-failures=")).data();
- init_failure_output_.reset(new std::ofstream(file_name));
- if (init_failure_output_.get() == nullptr) {
- LOG(ERROR) << "Failed to allocate ofstream";
- } else if (init_failure_output_->fail()) {
- LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization "
- << "failures.";
- init_failure_output_.reset();
+ if (IsAppImage() && IsBootImage()) {
+ Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
}
- }
-
- void ParseSwapFd(const StringPiece& option) {
- ParseUintOption(option, "--swap-fd", &swap_fd_);
- }
- void ProcessOptions(ParserOptions* parser_options) {
- image_ = (!image_filename_.empty());
- if (image_) {
+ if (IsBootImage()) {
// We need the boot image to always be debuggable.
- parser_options->debuggable = true;
+ compiler_options_->debuggable_ = true;
}
if (oat_filename_.empty() && oat_fd_ == -1) {
@@ -789,7 +658,7 @@ class Dex2Oat FINAL {
android_root_ += android_root_env_var;
}
- if (!image_ && parser_options->boot_image_filename.empty()) {
+ if (!boot_image_ && parser_options->boot_image_filename.empty()) {
parser_options->boot_image_filename += android_root_;
parser_options->boot_image_filename += "/framework/boot.art";
}
@@ -798,7 +667,7 @@ class Dex2Oat FINAL {
boot_image_option_ += parser_options->boot_image_filename;
}
- if (image_classes_filename_ != nullptr && !image_) {
+ if (image_classes_filename_ != nullptr && !IsBootImage()) {
Usage("--image-classes should only be used with --image");
}
@@ -810,7 +679,7 @@ class Dex2Oat FINAL {
Usage("--image-classes-zip should be used with --image-classes");
}
- if (compiled_classes_filename_ != nullptr && !image_) {
+ if (compiled_classes_filename_ != nullptr && !IsBootImage()) {
Usage("--compiled-classes should only be used with --image");
}
@@ -882,44 +751,19 @@ class Dex2Oat FINAL {
}
}
- if (parser_options->compiler_filter_string == nullptr) {
- parser_options->compiler_filter_string = "speed";
- }
-
- CHECK(parser_options->compiler_filter_string != nullptr);
- if (strcmp(parser_options->compiler_filter_string, "verify-none") == 0) {
- parser_options->compiler_filter = CompilerOptions::kVerifyNone;
- } else if (strcmp(parser_options->compiler_filter_string, "interpret-only") == 0) {
- parser_options->compiler_filter = CompilerOptions::kInterpretOnly;
- } else if (strcmp(parser_options->compiler_filter_string, "verify-at-runtime") == 0) {
- parser_options->compiler_filter = CompilerOptions::kVerifyAtRuntime;
- } else if (strcmp(parser_options->compiler_filter_string, "space") == 0) {
- parser_options->compiler_filter = CompilerOptions::kSpace;
- } else if (strcmp(parser_options->compiler_filter_string, "balanced") == 0) {
- parser_options->compiler_filter = CompilerOptions::kBalanced;
- } else if (strcmp(parser_options->compiler_filter_string, "speed") == 0) {
- parser_options->compiler_filter = CompilerOptions::kSpeed;
- } else if (strcmp(parser_options->compiler_filter_string, "everything") == 0) {
- parser_options->compiler_filter = CompilerOptions::kEverything;
- } else if (strcmp(parser_options->compiler_filter_string, "time") == 0) {
- parser_options->compiler_filter = CompilerOptions::kTime;
- } else {
- Usage("Unknown --compiler-filter value %s", parser_options->compiler_filter_string);
- }
-
// It they are not set, use default values for inlining settings.
// TODO: We should rethink the compiler filter. We mostly save
// time here, which is orthogonal to space.
- if (parser_options->inline_depth_limit == ParserOptions::kUnsetInlineDepthLimit) {
- parser_options->inline_depth_limit =
- (parser_options->compiler_filter == CompilerOptions::kSpace)
+ if (compiler_options_->inline_depth_limit_ == CompilerOptions::kUnsetInlineDepthLimit) {
+ compiler_options_->inline_depth_limit_ =
+ (compiler_options_->compiler_filter_ == CompilerOptions::kSpace)
// Implementation of the space filter: limit inlining depth.
? CompilerOptions::kSpaceFilterInlineDepthLimit
: CompilerOptions::kDefaultInlineDepthLimit;
}
- if (parser_options->inline_max_code_units == ParserOptions::kUnsetInlineMaxCodeUnits) {
- parser_options->inline_max_code_units =
- (parser_options->compiler_filter == CompilerOptions::kSpace)
+ if (compiler_options_->inline_max_code_units_ == CompilerOptions::kUnsetInlineMaxCodeUnits) {
+ compiler_options_->inline_max_code_units_ =
+ (compiler_options_->compiler_filter_ == CompilerOptions::kSpace)
// Implementation of the space filter: limit inlining max code units.
? CompilerOptions::kSpaceFilterInlineMaxCodeUnits
: CompilerOptions::kDefaultInlineMaxCodeUnits;
@@ -935,8 +779,8 @@ class Dex2Oat FINAL {
case kX86_64:
case kMips:
case kMips64:
- parser_options->implicit_null_checks = true;
- parser_options->implicit_so_checks = true;
+ compiler_options_->implicit_null_checks_ = true;
+ compiler_options_->implicit_so_checks_ = true;
break;
default:
@@ -944,29 +788,7 @@ class Dex2Oat FINAL {
break;
}
- compiler_options_.reset(new CompilerOptions(parser_options->compiler_filter,
- parser_options->huge_method_threshold,
- parser_options->large_method_threshold,
- parser_options->small_method_threshold,
- parser_options->tiny_method_threshold,
- parser_options->num_dex_methods_threshold,
- parser_options->inline_depth_limit,
- parser_options->inline_max_code_units,
- parser_options->include_patch_information,
- parser_options->top_k_profile_threshold,
- parser_options->debuggable,
- parser_options->generate_debug_info,
- parser_options->implicit_null_checks,
- parser_options->implicit_so_checks,
- parser_options->implicit_suspend_checks,
- parser_options->compile_pic,
- verbose_methods_.empty() ?
- nullptr :
- &verbose_methods_,
- new PassManagerOptions(
- parser_options->pass_manager_options),
- init_failure_output_.get(),
- parser_options->abort_on_hard_verifier_error));
+ compiler_options_->verbose_methods_ = verbose_methods_.empty() ? nullptr : &verbose_methods_;
// Done with usage checks, enable watchdog if requested
if (parser_options->watch_dog_enabled) {
@@ -977,7 +799,7 @@ class Dex2Oat FINAL {
key_value_store_.reset(new SafeMap<std::string, std::string>());
}
- void InsertCompileOptions(int argc, char** argv, ParserOptions* parser_options) {
+ void InsertCompileOptions(int argc, char** argv) {
std::ostringstream oss;
for (int i = 0; i < argc; ++i) {
if (i > 0) {
@@ -991,10 +813,10 @@ class Dex2Oat FINAL {
key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
key_value_store_->Put(
OatHeader::kPicKey,
- parser_options->compile_pic ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ compiler_options_->compile_pic_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
key_value_store_->Put(
OatHeader::kDebuggableKey,
- parser_options->debuggable ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
}
// Parse the arguments from the command line. In case of an unrecognized option or impossible
@@ -1015,6 +837,7 @@ class Dex2Oat FINAL {
}
std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
+ compiler_options_.reset(new CompilerOptions());
for (int i = 0; i < argc; i++) {
const StringPiece option(argv[i]);
@@ -1072,24 +895,11 @@ class Dex2Oat FINAL {
ParseInstructionSetFeatures(option, parser_options.get());
} else if (option.starts_with("--compiler-backend=")) {
ParseCompilerBackend(option, parser_options.get());
- } else if (option.starts_with("--compiler-filter=")) {
- parser_options->compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
- } else if (option == "--compile-pic") {
- parser_options->compile_pic = true;
- } else if (option.starts_with("--huge-method-max=")) {
- ParseHugeMethodMax(option, parser_options.get());
- } else if (option.starts_with("--large-method-max=")) {
- ParseLargeMethodMax(option, parser_options.get());
- } else if (option.starts_with("--small-method-max=")) {
- ParseSmallMethodMax(option, parser_options.get());
- } else if (option.starts_with("--tiny-method-max=")) {
- ParseTinyMethodMax(option, parser_options.get());
- } else if (option.starts_with("--num-dex-methods=")) {
- ParseNumDexMethods(option, parser_options.get());
- } else if (option.starts_with("--inline-depth-limit=")) {
- ParseInlineDepthLimit(option, parser_options.get());
- } else if (option.starts_with("--inline-max-code-units=")) {
- ParseInlineMaxCodeUnits(option, parser_options.get());
+ } else if (option.starts_with("--profile-file=")) {
+ profile_file_ = option.substr(strlen("--profile-file=")).data();
+ VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
+ } else if (option == "--no-profile-file") {
+ // No profile
} else if (option == "--host") {
is_host_ = true;
} else if (option == "--runtime-arg") {
@@ -1110,52 +920,20 @@ class Dex2Oat FINAL {
dump_cfg_append_ = true;
} else if (option == "--dump-stats") {
dump_stats_ = true;
- } else if (option == "--generate-debug-info" || option == "-g") {
- parser_options->generate_debug_info = true;
- } else if (option == "--no-generate-debug-info") {
- parser_options->generate_debug_info = false;
- } else if (option == "--debuggable") {
- parser_options->debuggable = true;
- parser_options->generate_debug_info = true;
- } else if (option.starts_with("--profile-file=")) {
- profile_file_ = option.substr(strlen("--profile-file=")).data();
- VLOG(compiler) << "dex2oat: profile file is " << profile_file_;
- } else if (option == "--no-profile-file") {
- // No profile
- } else if (option.starts_with("--top-k-profile-threshold=")) {
- ParseDouble(option.data(), '=', 0.0, 100.0, &parser_options->top_k_profile_threshold);
- } else if (option == "--print-pass-names") {
- parser_options->pass_manager_options.SetPrintPassNames(true);
- } else if (option.starts_with("--disable-passes=")) {
- ParseDisablePasses(option, parser_options.get());
- } else if (option.starts_with("--print-passes=")) {
- ParsePrintPasses(option, parser_options.get());
- } else if (option == "--print-all-passes") {
- parser_options->pass_manager_options.SetPrintAllPasses();
- } else if (option.starts_with("--dump-cfg-passes=")) {
- ParseDumpCfgPasses(option, parser_options.get());
- } else if (option == "--print-pass-options") {
- parser_options->pass_manager_options.SetPrintPassOptions(true);
- } else if (option.starts_with("--pass-options=")) {
- ParsePassOptions(option, parser_options.get());
- } else if (option == "--include-patch-information") {
- parser_options->include_patch_information = true;
- } else if (option == "--no-include-patch-information") {
- parser_options->include_patch_information = false;
+ } else if (option.starts_with("--swap-file=")) {
+ swap_file_name_ = option.substr(strlen("--swap-file=")).data();
+ } else if (option.starts_with("--swap-fd=")) {
+ ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
+ } else if (option.starts_with("--app-image-file=")) {
+ app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
+ } else if (option.starts_with("--app-image-fd=")) {
+ ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
} else if (option.starts_with("--verbose-methods=")) {
// TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
// conditional on having verbost methods.
gLogVerbosity.compiler = false;
Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
- } else if (option.starts_with("--dump-init-failures=")) {
- ParseDumpInitFailures(option);
- } else if (option.starts_with("--swap-file=")) {
- swap_file_name_ = option.substr(strlen("--swap-file=")).data();
- } else if (option.starts_with("--swap-fd=")) {
- ParseSwapFd(option);
- } else if (option == "--abort-on-hard-verifier-error") {
- parser_options->abort_on_hard_verifier_error = true;
- } else {
+ } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
}
@@ -1163,7 +941,7 @@ class Dex2Oat FINAL {
ProcessOptions(parser_options.get());
// Insert some compiler things.
- InsertCompileOptions(argc, argv, parser_options.get());
+ InsertCompileOptions(argc, argv);
}
// Check whether the oat output file is writable, and open it for later. Also open a swap file,
@@ -1211,7 +989,6 @@ class Dex2Oat FINAL {
// released immediately.
unlink(swap_file_name_.c_str());
}
-
return true;
}
@@ -1253,7 +1030,7 @@ class Dex2Oat FINAL {
callbacks_.reset(new QuickCompilerCallbacks(
verification_results_.get(),
&method_inliner_map_,
- image_ ?
+ IsBootImage() ?
CompilerCallbacks::CallbackMode::kCompileBootImage :
CompilerCallbacks::CallbackMode::kCompileApp));
runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
@@ -1263,7 +1040,7 @@ class Dex2Oat FINAL {
// Only allow no boot image for the runtime if we're compiling one. When we compile an app,
// we don't want fallback mode, it will abort as we do not push a boot classpath (it might
// have been stripped in preopting, anyways).
- if (!image_) {
+ if (!IsBootImage()) {
runtime_options.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr));
}
// Disable libsigchain. We don't don't need it during compilation and it prevents us
@@ -1302,7 +1079,7 @@ class Dex2Oat FINAL {
"': " << error_msg;
return false;
}
- } else if (image_) {
+ } else if (IsBootImage()) {
image_classes_.reset(new std::unordered_set<std::string>);
}
// If --compiled-classes was specified, calculate the full list of classes to compile in the
@@ -1410,11 +1187,12 @@ class Dex2Oat FINAL {
ScopedObjectAccess soa(self);
dex_caches_.push_back(soa.AddLocalReference<jobject>(
class_linker->RegisterDexFile(*dex_file, Runtime::Current()->GetLinearAlloc())));
+ dex_file->CreateTypeLookupTable();
}
// If we use a swap file, ensure we are above the threshold to make it necessary.
if (swap_fd_ != -1) {
- if (!UseSwap(image_, dex_files_)) {
+ if (!UseSwap(IsBootImage(), dex_files_)) {
close(swap_fd_);
swap_fd_ = -1;
VLOG(compiler) << "Decided to run without swap.";
@@ -1428,7 +1206,7 @@ class Dex2Oat FINAL {
* If we're not in interpret-only or verify-none mode, go ahead and compile small applications.
* Don't bother to check if we're doing the image.
*/
- if (!image_ &&
+ if (!IsBootImage() &&
compiler_options_->IsCompilationEnabled() &&
compiler_kind_ == Compiler::kQuick) {
size_t num_methods = 0;
@@ -1482,7 +1260,7 @@ class Dex2Oat FINAL {
compiler_kind_,
instruction_set_,
instruction_set_features_.get(),
- image_,
+ IsBootImage(),
image_classes_.release(),
compiled_classes_.release(),
nullptr,
@@ -1495,6 +1273,7 @@ class Dex2Oat FINAL {
swap_fd_,
profile_file_));
+ driver_->SetDexFilesForOatFile(dex_files_);
driver_->CompileAll(class_loader, dex_files_, timings_);
}
@@ -1576,7 +1355,7 @@ class Dex2Oat FINAL {
uint32_t image_file_location_oat_checksum = 0;
uintptr_t image_file_location_oat_data_begin = 0;
int32_t image_patch_delta = 0;
- if (image_) {
+ if (IsImage()) {
PrepareImageWriter(image_base_);
} else {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
@@ -1601,7 +1380,7 @@ class Dex2Oat FINAL {
key_value_store_.get()));
}
- if (image_) {
+ if (IsImage()) {
// The OatWriter constructor has already updated offsets in methods and we need to
// prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -1626,7 +1405,7 @@ class Dex2Oat FINAL {
// If we are compiling an image, invoke the image creation routine. Else just skip.
bool HandleImage() {
- if (image_) {
+ if (IsImage()) {
TimingLogger::ScopedTiming t("dex2oat ImageWriter", timings_);
if (!CreateImageFile()) {
return false;
@@ -1709,7 +1488,15 @@ class Dex2Oat FINAL {
}
bool IsImage() const {
- return image_;
+ return IsAppImage() || IsBootImage();
+ }
+
+ bool IsAppImage() const {
+ return app_image_;
+ }
+
+ bool IsBootImage() const {
+ return boot_image_;
}
bool IsHost() const {
@@ -1811,7 +1598,10 @@ class Dex2Oat FINAL {
bool CreateImageFile()
REQUIRES(!Locks::mutator_lock_) {
CHECK(image_writer_ != nullptr);
- if (!image_writer_->Write(image_filename_, oat_unstripped_, oat_location_)) {
+ if (!image_writer_->Write(app_image_fd_,
+ IsBootImage() ? image_filename_ : app_image_file_name_,
+ oat_unstripped_,
+ oat_location_)) {
LOG(ERROR) << "Failed to create image file " << image_filename_;
return false;
}
@@ -1820,8 +1610,8 @@ class Dex2Oat FINAL {
// Destroy ImageWriter before doing FixupElf.
image_writer_.reset();
- // Do not fix up the ELF file if we are --compile-pic
- if (!compiler_options_->GetCompilePic()) {
+ // Do not fix up the ELF file if we are --compile-pic or compiing the app image
+ if (!compiler_options_->GetCompilePic() && IsBootImage()) {
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str()));
if (oat_file.get() == nullptr) {
PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_;
@@ -1983,7 +1773,8 @@ class Dex2Oat FINAL {
std::unique_ptr<std::unordered_set<std::string>> image_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_methods_;
- bool image_;
+ bool app_image_;
+ bool boot_image_;
bool is_host_;
std::string android_root_;
std::vector<const DexFile*> dex_files_;
@@ -2002,10 +1793,11 @@ class Dex2Oat FINAL {
bool dump_cfg_append_;
std::string swap_file_name_;
int swap_fd_;
+ std::string app_image_file_name_;
+ int app_image_fd_;
std::string profile_file_; // Profile file to use
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
- std::unique_ptr<std::ostream> init_failure_output_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
@@ -2131,7 +1923,7 @@ static int dex2oat(int argc, char** argv) {
// 3) Compiling with --host
// 4) Compiling on the host (not a target build)
// Otherwise, print a stripped command line.
- if (kIsDebugBuild || dex2oat.IsImage() || dex2oat.IsHost() || !kIsTargetBuild) {
+ if (kIsDebugBuild || dex2oat.IsBootImage() || dex2oat.IsHost() || !kIsTargetBuild) {
LOG(INFO) << CommandLine();
} else {
LOG(INFO) << StrippedCommandLine();
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 282db5de83..52e6c023fe 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -775,7 +775,7 @@ static void dumpInstruction(const DexFile* pDexFile,
// case Instruction::k35ms: // [opt] invoke-virtual+super
// case Instruction::k35mi: // [opt] inline invoke
{
- u4 arg[5];
+ u4 arg[Instruction::kMaxVarArgRegs];
pDecInsn->GetVarArgs(arg);
fputs(" {", gOutFile);
for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) {
@@ -788,6 +788,21 @@ static void dumpInstruction(const DexFile* pDexFile,
fprintf(gOutFile, "}, %s", indexBuf);
}
break;
+ case Instruction::k25x: // op vC, {vD, vE, vF, vG} (B: count)
+ {
+ u4 arg[Instruction::kMaxVarArgRegs25x];
+ pDecInsn->GetAllArgs25x(arg);
+ fprintf(gOutFile, " v%d, {", arg[0]);
+ for (int i = 0, n = pDecInsn->VRegB(); i < n; i++) {
+ if (i == 0) {
+ fprintf(gOutFile, "v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]);
+ } else {
+ fprintf(gOutFile, ", v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]);
+ }
+ } // for
+ fputc('}', gOutFile);
+ }
+ break;
case Instruction::k3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
// NOT SUPPORTED:
// case Instruction::k3rms: // [opt] invoke-virtual+super/range
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 09d73119e6..1fdffe3e17 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -47,6 +47,7 @@ LIBART_COMMON_SRC_FILES := \
dex_file_verifier.cc \
dex_instruction.cc \
elf_file.cc \
+ fault_handler.cc \
gc/allocation_record.cc \
gc/allocator/dlmalloc.cc \
gc/allocator/rosalloc.cc \
@@ -162,6 +163,7 @@ LIBART_COMMON_SRC_FILES := \
os_linux.cc \
parsed_options.cc \
primitive.cc \
+ profiler.cc \
quick_exception_handler.cc \
quick/inline_method_analyser.cc \
reference_table.cc \
@@ -176,8 +178,7 @@ LIBART_COMMON_SRC_FILES := \
thread_pool.cc \
trace.cc \
transaction.cc \
- profiler.cc \
- fault_handler.cc \
+ type_lookup_table.cc \
utf.cc \
utils.cc \
verifier/dex_gc_map.cc \
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index be5a15ec39..9ccabad1cc 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1437,7 +1437,107 @@ END art_quick_set64_static
ONE_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Generate the allocation entrypoints for each allocator.
-GENERATE_ALL_ALLOC_ENTRYPOINTS
+GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_tlab, TLAB)
+// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT(_rosalloc, RosAlloc).
+ENTRY art_quick_alloc_object_rosalloc
+ // Fast path rosalloc allocation.
+ // x0: type_idx/return value, x1: ArtMethod*, xSELF(x19): Thread::Current
+ // x2-x7: free.
+ ldr x2, [x1, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64] // Load dex cache resolved types array
+ // Load the class (x2)
+ ldr w2, [x2, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
+ cbz x2, .Lart_quick_alloc_object_rosalloc_slow_path // Check null class
+ // Check class status.
+ ldr w3, [x2, #MIRROR_CLASS_STATUS_OFFSET]
+ cmp x3, #MIRROR_CLASS_STATUS_INITIALIZED
+ bne .Lart_quick_alloc_object_rosalloc_slow_path
+ // Add a fake dependence from the
+ // following access flag and size
+ // loads to the status load.
+ // This is to prevent those loads
+ // from being reordered above the
+ // status load and reading wrong
+ // values (an alternative is to use
+ // a load-acquire for the status).
+ eor x3, x3, x3
+ add x2, x2, x3
+ // Check access flags has
+ // kAccClassIsFinalizable
+ ldr w3, [x2, #MIRROR_CLASS_ACCESS_FLAGS_OFFSET]
+ tst x3, #ACCESS_FLAGS_CLASS_IS_FINALIZABLE
+ bne .Lart_quick_alloc_object_rosalloc_slow_path
+ ldr x3, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET] // Check if the thread local
+ // allocation stack has room.
+ // ldp won't work due to large offset.
+ ldr x4, [xSELF, #THREAD_LOCAL_ALLOC_STACK_END_OFFSET]
+ cmp x3, x4
+ bhs .Lart_quick_alloc_object_rosalloc_slow_path
+ ldr w3, [x2, #MIRROR_CLASS_OBJECT_SIZE_OFFSET] // Load the object size (x3)
+ cmp x3, #ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE // Check if the size is for a thread
+ // local allocation
+ bhs .Lart_quick_alloc_object_rosalloc_slow_path
+ // Compute the rosalloc bracket index
+ // from the size.
+ // Align up the size by the rosalloc
+ // bracket quantum size and divide
+ // by the quantum size and subtract
+ // by 1. This code is a shorter but
+ // equivalent version.
+ sub x3, x3, #1
+ lsr x3, x3, #ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT
+ // Load the rosalloc run (x4)
+ add x4, xSELF, x3, lsl #POINTER_SIZE_SHIFT
+ ldr x4, [x4, #THREAD_ROSALLOC_RUNS_OFFSET]
+ // Load the free list head (x3). This
+ // will be the return val.
+ ldr x3, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
+ cbz x3, .Lart_quick_alloc_object_rosalloc_slow_path
+ // "Point of no slow path". Won't go to the slow path from here on. OK to clobber x0 and x1.
+ ldr x1, [x3, #ROSALLOC_SLOT_NEXT_OFFSET] // Load the next pointer of the head
+ // and update the list head with the
+ // next pointer.
+ str x1, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)]
+ // Store the class pointer in the
+ // header. This also overwrites the
+ // next pointer. The offsets are
+ // asserted to match.
+#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET
+#error "Class pointer needs to overwrite next pointer."
+#endif
+ POISON_HEAP_REF w2
+ str w2, [x3, #MIRROR_OBJECT_CLASS_OFFSET]
+ // Push the new object onto the thread
+ // local allocation stack and
+ // increment the thread local
+ // allocation stack top.
+ ldr x1, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
+ str w3, [x1], #COMPRESSED_REFERENCE_SIZE // (Increment x1 as a side effect.)
+ str x1, [xSELF, #THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET]
+ // Decrement the size of the free list
+ ldr w1, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)]
+ sub x1, x1, #1
+ // TODO: consider combining this store
+ // and the list head store above using
+ // strd.
+ str w1, [x4, #(ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)]
+ // Fence. This is "ish" not "ishst" so
+ // that the code after this allocation
+ // site will see the right values in
+ // the fields of the class.
+ // Alternatively we could use "ishst"
+ // if we use load-acquire for the
+ // class status load.)
+ dmb ish
+ mov x0, x3 // Set the return value and return.
+ ret
+.Lart_quick_alloc_object_rosalloc_slow_path:
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME // save callee saves in case of GC
+ mov x2, xSELF // pass Thread::Current
+ bl artAllocObjectFromCodeRosAlloc // (uint32_t type_idx, Method* method, Thread*)
+ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+END art_quick_alloc_object_rosalloc
/*
* Called by managed code when the thread has been asked to suspend.
diff --git a/runtime/arch/mips/context_mips.cc b/runtime/arch/mips/context_mips.cc
index 4dedb3339e..375a03acee 100644
--- a/runtime/arch/mips/context_mips.cc
+++ b/runtime/arch/mips/context_mips.cc
@@ -28,11 +28,11 @@ void MipsContext::Reset() {
std::fill_n(gprs_, arraysize(gprs_), nullptr);
std::fill_n(fprs_, arraysize(fprs_), nullptr);
gprs_[SP] = &sp_;
- gprs_[RA] = &ra_;
+ gprs_[T9] = &t9_;
gprs_[A0] = &arg0_;
// Initialize registers with easy to spot debug values.
sp_ = MipsContext::kBadGprBase + SP;
- ra_ = MipsContext::kBadGprBase + RA;
+ t9_ = MipsContext::kBadGprBase + T9;
arg0_ = 0;
}
diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h
index f1e2905592..7dcff630d1 100644
--- a/runtime/arch/mips/context_mips.h
+++ b/runtime/arch/mips/context_mips.h
@@ -41,7 +41,7 @@ class MipsContext : public Context {
}
void SetPC(uintptr_t new_pc) OVERRIDE {
- SetGPR(RA, new_pc);
+ SetGPR(T9, new_pc);
}
bool IsAccessibleGPR(uint32_t reg) OVERRIDE {
@@ -86,9 +86,10 @@ class MipsContext : public Context {
// Pointers to registers in the stack, initialized to null except for the special cases below.
uintptr_t* gprs_[kNumberOfCoreRegisters];
uint32_t* fprs_[kNumberOfFRegisters];
- // Hold values for sp and ra (return address) if they are not located within a stack frame, as
- // well as the first argument.
- uintptr_t sp_, ra_, arg0_;
+ // Hold values for sp and t9 if they are not located within a stack frame. We use t9 for the
+ // PC (as ra is required to be valid for single-frame deopt and must not be clobbered). We
+ // also need the first argument for single-frame deopt.
+ uintptr_t sp_, t9_, arg0_;
};
} // namespace mips
} // namespace art
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index ba58c3fccb..0691f2a620 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -374,7 +374,7 @@ ENTRY art_quick_do_long_jump
lw $ra, 124($a0)
lw $a0, 16($a0)
move $v0, $zero # clear result registers r0 and r1
- jalr $zero, $ra # do long jump
+ jalr $zero, $t9 # do long jump
move $v1, $zero
END art_quick_do_long_jump
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index f741732046..cf548ada33 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -468,12 +468,6 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
}
}
-inline void ArtMethod::CopyFrom(const ArtMethod* src, size_t image_pointer_size) {
- memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
- Size(image_pointer_size));
- declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
-}
-
} // namespace art
#endif // ART_RUNTIME_ART_METHOD_INL_H_
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index f5befdfc07..f4a5f233ff 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -163,18 +163,13 @@ uint32_t ArtMethod::FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfil
return dex_method_idx;
}
const char* mid_declaring_class_descriptor = dexfile->StringByTypeIdx(mid.class_idx_);
- const DexFile::StringId* other_descriptor =
- other_dexfile.FindStringId(mid_declaring_class_descriptor);
- if (other_descriptor != nullptr) {
- const DexFile::TypeId* other_type_id =
- other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor));
- if (other_type_id != nullptr) {
- const DexFile::MethodId* other_mid = other_dexfile.FindMethodId(
- *other_type_id, other_dexfile.GetStringId(name_and_sig_mid.name_idx_),
- other_dexfile.GetProtoId(name_and_sig_mid.proto_idx_));
- if (other_mid != nullptr) {
- return other_dexfile.GetIndexForMethodId(*other_mid);
- }
+ const DexFile::TypeId* other_type_id = other_dexfile.FindTypeId(mid_declaring_class_descriptor);
+ if (other_type_id != nullptr) {
+ const DexFile::MethodId* other_mid = other_dexfile.FindMethodId(
+ *other_type_id, other_dexfile.GetStringId(name_and_sig_mid.name_idx_),
+ other_dexfile.GetProtoId(name_and_sig_mid.proto_idx_));
+ if (other_mid != nullptr) {
+ return other_dexfile.GetIndexForMethodId(*other_mid);
}
}
return DexFile::kDexNoIndex;
@@ -361,19 +356,6 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param
return true;
}
-ProfilingInfo* ArtMethod::CreateProfilingInfo() {
- DCHECK(!Runtime::Current()->IsAotCompiler());
- ProfilingInfo* info = ProfilingInfo::Create(this);
- MemberOffset offset = ArtMethod::EntryPointFromJniOffset(sizeof(void*));
- uintptr_t pointer = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
- if (!reinterpret_cast<Atomic<ProfilingInfo*>*>(pointer)->
- CompareExchangeStrongSequentiallyConsistent(nullptr, info)) {
- return GetProfilingInfo(sizeof(void*));
- } else {
- return info;
- }
-}
-
const uint8_t* ArtMethod::GetQuickenedInfo() {
bool found = false;
OatFile::OatMethod oat_method =
@@ -385,27 +367,119 @@ const uint8_t* ArtMethod::GetQuickenedInfo() {
}
const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) {
- if (IsRuntimeMethod() || IsProxyMethod()) {
+ if (IsRuntimeMethod()) {
return nullptr;
}
Runtime* runtime = Runtime::Current();
- const void* code = runtime->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*));
- DCHECK(code != nullptr);
+ const void* existing_entry_point = GetEntryPointFromQuickCompiledCode();
+ DCHECK(existing_entry_point != nullptr);
+ ClassLinker* class_linker = runtime->GetClassLinker();
- if (runtime->GetClassLinker()->IsQuickGenericJniStub(code)) {
+ if (class_linker->IsQuickGenericJniStub(existing_entry_point)) {
// The generic JNI does not have any method header.
return nullptr;
}
- code = EntryPointToCodePointer(code);
- OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
- reinterpret_cast<uintptr_t>(code) - sizeof(OatQuickMethodHeader));
+ if (existing_entry_point == GetQuickProxyInvokeHandler()) {
+ DCHECK(IsProxyMethod() && !IsConstructor());
+ // The proxy entry point does not have any method header.
+ return nullptr;
+ }
- // TODO(ngeoffray): validate the pc. Note that unit tests can give unrelated pcs (for
- // example arch_test).
- UNUSED(pc);
+ // Check whether the current entry point contains this pc.
+ if (!class_linker->IsQuickResolutionStub(existing_entry_point) &&
+ !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) {
+ OatQuickMethodHeader* method_header =
+ OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
+
+ if (method_header->Contains(pc)) {
+ return method_header;
+ }
+ }
+
+ // Check whether the pc is in the JIT code cache.
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ OatQuickMethodHeader* method_header = code_cache->LookupMethodHeader(pc, this);
+ if (method_header != nullptr) {
+ DCHECK(method_header->Contains(pc));
+ return method_header;
+ } else {
+ DCHECK(!code_cache->ContainsPc(reinterpret_cast<const void*>(pc))) << std::hex << pc;
+ }
+ }
+
+ // The code has to be in an oat file.
+ bool found;
+ OatFile::OatMethod oat_method = class_linker->FindOatMethodFor(this, &found);
+ if (!found) {
+ if (class_linker->IsQuickResolutionStub(existing_entry_point)) {
+ // We are running the generic jni stub, but the entry point of the method has not
+ // been updated yet.
+ DCHECK_EQ(pc, 0u) << "Should be a downcall";
+ DCHECK(IsNative());
+ return nullptr;
+ }
+ if (existing_entry_point == GetQuickInstrumentationEntryPoint()) {
+ // We are running the generic jni stub, but the method is being instrumented.
+ DCHECK_EQ(pc, 0u) << "Should be a downcall";
+ DCHECK(IsNative());
+ return nullptr;
+ }
+ // Only for unit tests.
+ // TODO(ngeoffray): Update these tests to pass the right pc?
+ return OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
+ }
+ const void* oat_entry_point = oat_method.GetQuickCode();
+ if (oat_entry_point == nullptr || class_linker->IsQuickGenericJniStub(oat_entry_point)) {
+ DCHECK(IsNative());
+ return nullptr;
+ }
+
+ OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(oat_entry_point);
+ if (pc == 0) {
+ // This is a downcall, it can only happen for a native method.
+ DCHECK(IsNative());
+ return method_header;
+ }
+
+ if (pc == reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc())) {
+ // If we're instrumenting, just return the compiled OAT code.
+ // TODO(ngeoffray): Avoid this call path.
+ return method_header;
+ }
+
+ DCHECK(method_header->Contains(pc))
+ << PrettyMethod(this)
+ << std::hex << pc << " " << oat_entry_point
+ << " " << (uintptr_t)(method_header->code_ + method_header->code_size_);
return method_header;
}
+
+void ArtMethod::CopyFrom(ArtMethod* src, size_t image_pointer_size) {
+ memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src),
+ Size(image_pointer_size));
+ declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
+
+ // If the entry point of the method we are copying from is from JIT code, we just
+ // put the entry point of the new method to interpreter. We could set the entry point
+ // to the JIT code, but this would require taking the JIT code cache lock to notify
+ // it, which we do not want at this level.
+ Runtime* runtime = Runtime::Current();
+ if (runtime->GetJit() != nullptr) {
+ if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
+ SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
+ }
+ }
+ // Clear the profiling info for the same reasons as the JIT code.
+ if (!src->IsNative()) {
+ SetProfilingInfoPtrSize(nullptr, image_pointer_size);
+ }
+ // Clear hotness to let the JIT properly decide when to compile this method.
+ hotness_count_ = 0;
+}
+
} // namespace art
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 9f1495cf39..ce9f2025ce 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -49,8 +49,8 @@ class ArtMethod FINAL {
ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
method_index_(0) { }
- ArtMethod(const ArtMethod& src, size_t image_pointer_size) {
- CopyFrom(&src, image_pointer_size);
+ ArtMethod(ArtMethod* src, size_t image_pointer_size) {
+ CopyFrom(src, image_pointer_size);
}
static ArtMethod* FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa,
@@ -305,12 +305,22 @@ class ArtMethod FINAL {
PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
}
- ProfilingInfo* CreateProfilingInfo() SHARED_REQUIRES(Locks::mutator_lock_);
-
ProfilingInfo* GetProfilingInfo(size_t pointer_size) {
return reinterpret_cast<ProfilingInfo*>(GetEntryPointFromJniPtrSize(pointer_size));
}
+ ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
+ SetEntryPointFromJniPtrSize(info, sizeof(void*));
+ }
+
+ ALWAYS_INLINE void SetProfilingInfoPtrSize(ProfilingInfo* info, size_t pointer_size) {
+ SetEntryPointFromJniPtrSize(info, pointer_size);
+ }
+
+ static MemberOffset ProfilingInfoOffset() {
+ return EntryPointFromJniOffset(sizeof(void*));
+ }
+
void* GetEntryPointFromJni() {
return GetEntryPointFromJniPtrSize(sizeof(void*));
}
@@ -423,7 +433,7 @@ class ArtMethod FINAL {
return pointer_size;
}
- void CopyFrom(const ArtMethod* src, size_t image_pointer_size)
+ void CopyFrom(ArtMethod* src, size_t image_pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
ALWAYS_INLINE GcRoot<mirror::Class>* GetDexCacheResolvedTypes(size_t pointer_size)
@@ -433,6 +443,10 @@ class ArtMethod FINAL {
return ++hotness_count_;
}
+ void ClearCounter() {
+ hotness_count_ = 0;
+ }
+
const uint8_t* GetQuickenedInfo() SHARED_REQUIRES(Locks::mutator_lock_);
// Returns the method header for the compiled code containing 'pc'. Note that runtime
diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h
index ad255b8694..969f5b953f 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -22,6 +22,7 @@
#include <unordered_map>
#include "atomic.h"
+#include "base/hash_map.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "base/type_static_if.h"
@@ -170,6 +171,14 @@ template<class Key,
using AllocationTrackingUnorderedMap = std::unordered_map<
Key, T, Hash, Pred, TrackingAllocator<std::pair<const Key, T>, kTag>>;
+template<class Key,
+ class T,
+ class EmptyFn,
+ AllocatorTag kTag,
+ class Hash = std::hash<Key>,
+ class Pred = std::equal_to<Key>>
+using AllocationTrackingHashMap = HashMap<
+ Key, T, EmptyFn, Hash, Pred, TrackingAllocator<std::pair<Key, T>, kTag>>;
} // namespace art
#endif // ART_RUNTIME_BASE_ALLOCATOR_H_
diff --git a/runtime/base/dchecked_vector.h b/runtime/base/dchecked_vector.h
index 6ec573a5fb..2bd12df2c3 100644
--- a/runtime/base/dchecked_vector.h
+++ b/runtime/base/dchecked_vector.h
@@ -59,8 +59,10 @@ class dchecked_vector : private std::vector<T, Alloc> {
: Base() { }
explicit dchecked_vector(const allocator_type& alloc)
: Base(alloc) { }
+ // Note that we cannot forward to std::vector(size_type, const allocator_type&) because it is not
+ // available in C++11, which is the latest GCC can support. http://b/25022512
explicit dchecked_vector(size_type n, const allocator_type& alloc = allocator_type())
- : Base(n, alloc) { }
+ : Base(alloc) { resize(n); }
dchecked_vector(size_type n,
const value_type& value,
const allocator_type& alloc = allocator_type())
diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h
index 4819f06bb4..95baa822b1 100644
--- a/runtime/base/hash_set.h
+++ b/runtime/base/hash_set.h
@@ -420,6 +420,19 @@ class HashSet {
Resize(Size() / max_load_factor_);
}
+ // Reserve enough room to insert until Size() == num_elements without requiring to grow the hash
+ // set. No-op if the hash set is already large enough to do this.
+ void Reserve(size_t num_elements) {
+ size_t num_buckets = num_elements / max_load_factor_;
+ // Deal with rounding errors. Add one for rounding.
+ while (static_cast<size_t>(num_buckets * max_load_factor_) <= num_elements + 1u) {
+ ++num_buckets;
+ }
+ if (num_buckets > NumBuckets()) {
+ Resize(num_buckets);
+ }
+ }
+
// To distance that inserted elements were probed. Used for measuring how good hash functions
// are.
size_t TotalProbeDistance() const {
@@ -488,6 +501,15 @@ class HashSet {
}
}
+ // The hash set expands when Size() reaches ElementsUntilExpand().
+ size_t ElementsUntilExpand() const {
+ return elements_until_expand_;
+ }
+
+ size_t NumBuckets() const {
+ return num_buckets_;
+ }
+
private:
T& ElementForIndex(size_t index) {
DCHECK_LT(index, NumBuckets());
@@ -543,10 +565,6 @@ class HashSet {
return emptyfn_.IsEmpty(ElementForIndex(index));
}
- size_t NumBuckets() const {
- return num_buckets_;
- }
-
// Allocate a number of buckets.
void AllocateStorage(size_t num_buckets) {
num_buckets_ = num_buckets;
diff --git a/runtime/base/hash_set_test.cc b/runtime/base/hash_set_test.cc
index 743e98ed84..825406313a 100644
--- a/runtime/base/hash_set_test.cc
+++ b/runtime/base/hash_set_test.cc
@@ -333,4 +333,25 @@ TEST_F(HashSetTest, TestLookupByAlternateKeyType) {
ASSERT_NE(hash_set.end(), hash_set.Find(std::forward_list<int>({1, 2, 3, 4})));
}
+TEST_F(HashSetTest, TestReserve) {
+ HashSet<std::string, IsEmptyFnString> hash_set;
+ std::vector<size_t> sizes = {1, 10, 25, 55, 128, 1024, 4096};
+ for (size_t size : sizes) {
+ hash_set.Reserve(size);
+ const size_t buckets_before = hash_set.NumBuckets();
+ // Check that we expanded enough.
+ CHECK_GE(hash_set.ElementsUntilExpand(), size);
+ // Try inserting elements until we are at our reserve size and ensure the hash set did not
+ // expand.
+ while (hash_set.Size() < size) {
+ hash_set.Insert(std::to_string(hash_set.Size()));
+ }
+ CHECK_EQ(hash_set.NumBuckets(), buckets_before);
+ }
+ // Check the behaviour for shrinking, it does not necessarily resize down.
+ constexpr size_t size = 100;
+ hash_set.Reserve(size);
+ CHECK_GE(hash_set.ElementsUntilExpand(), size);
+}
+
} // namespace art
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index f1f6f9b1c1..1942e1dc1b 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -125,7 +125,7 @@ void CumulativeLogger::DumpHistogram(std::ostream &os) const {
histogram->CreateHistogram(&cumulative_data);
histogram->PrintConfidenceIntervals(os, 0.99, cumulative_data);
}
- os << "Done Dumping histograms \n";
+ os << "Done Dumping histograms\n";
}
TimingLogger::TimingLogger(const char* name, bool precise, bool verbose)
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 81622e14ed..da70456369 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -616,10 +616,7 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
// initialized.
{
const DexFile& dex_file = java_lang_Object->GetDexFile();
- const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
- CHECK(void_string_id != nullptr);
- uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id);
- const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index);
+ const DexFile::TypeId* void_type_id = dex_file.FindTypeId("V");
CHECK(void_type_id != nullptr);
uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
// Now we resolve void type so the dex cache contains it. We use java.lang.Object class
@@ -1174,15 +1171,26 @@ ClassLinker::~ClassLinker() {
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
Thread* const self = Thread::Current();
- JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
for (const ClassLoaderData& data : class_loaders_) {
- vm->DeleteWeakGlobalRef(self, data.weak_root);
- delete data.allocator;
- delete data.class_table;
+ DeleteClassLoader(self, data);
}
class_loaders_.clear();
}
+void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) {
+ Runtime* const runtime = Runtime::Current();
+ JavaVMExt* const vm = runtime->GetJavaVM();
+ vm->DeleteWeakGlobalRef(self, data.weak_root);
+ if (runtime->GetJit() != nullptr) {
+ jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
+ if (code_cache != nullptr) {
+ code_cache->RemoveMethodsIn(self, *data.allocator);
+ }
+ }
+ delete data.allocator;
+ delete data.class_table;
+}
+
mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
return down_cast<mirror::PointerArray*>(image_pointer_size_ == 8u ?
static_cast<mirror::Array*>(mirror::LongArray::Alloc(self, length)) :
@@ -1833,13 +1841,6 @@ const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
return code;
}
}
- jit::Jit* const jit = Runtime::Current()->GetJit();
- if (jit != nullptr) {
- auto* code = jit->GetCodeCache()->GetCodeFor(method);
- if (code != nullptr) {
- return code;
- }
- }
if (method->IsNative()) {
// No code and native? Use generic trampoline.
return GetQuickGenericJniStub();
@@ -1856,13 +1857,6 @@ const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) {
if (found) {
return oat_method.GetQuickCode();
}
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr) {
- auto* code = jit->GetCodeCache()->GetCodeFor(method);
- if (code != nullptr) {
- return code;
- }
- }
return nullptr;
}
@@ -2743,17 +2737,13 @@ mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
const DexFile* dex_file = dex_cache->GetDexFile();
- // Try binary searching the string/type index.
- const DexFile::StringId* string_id = dex_file->FindStringId(descriptor);
- if (string_id != nullptr) {
- const DexFile::TypeId* type_id =
- dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id));
- if (type_id != nullptr) {
- uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
- mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
- if (klass != nullptr) {
- return klass;
- }
+ // Try binary searching the type index by descriptor.
+ const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor);
+ if (type_id != nullptr) {
+ uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id);
+ mirror::Class* klass = dex_cache->GetResolvedType(type_idx);
+ if (klass != nullptr) {
+ return klass;
}
}
}
@@ -5289,7 +5279,7 @@ bool ClassLinker::LinkInterfaceMethods(
miranda_method = reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
CHECK(miranda_method != nullptr);
// Point the interface table at a phantom slot.
- new(miranda_method) ArtMethod(*interface_method, image_pointer_size_);
+ new(miranda_method) ArtMethod(interface_method, image_pointer_size_);
miranda_methods.push_back(miranda_method);
}
method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_);
@@ -5325,7 +5315,7 @@ bool ClassLinker::LinkInterfaceMethods(
ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table(allocator.Adapter());
if (virtuals != old_virtuals) {
// Maps from heap allocated miranda method to linear alloc miranda method.
- StrideIterator<ArtMethod> out = virtuals->Begin(method_size, method_alignment);
+ StrideIterator<ArtMethod> out = virtuals->begin(method_size, method_alignment);
// Copy over the old methods + miranda methods.
for (auto& m : klass->GetVirtualMethods(image_pointer_size_)) {
move_table.emplace(&m, &*out);
@@ -5335,7 +5325,7 @@ bool ClassLinker::LinkInterfaceMethods(
++out;
}
}
- StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment)
+ StrideIterator<ArtMethod> out(virtuals->begin(method_size, method_alignment)
+ old_method_count);
// Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and
// we want the roots of the miranda methods to get visited.
@@ -5367,7 +5357,7 @@ bool ClassLinker::LinkInterfaceMethods(
move_table.emplace(def_method, &new_method);
++out;
}
- virtuals->SetLength(new_method_count);
+ virtuals->SetSize(new_method_count);
UpdateClassVirtualMethods(klass.Get(), virtuals);
// Done copying methods, they are all roots in the class now, so we can end the no thread
// suspension assert.
@@ -5382,7 +5372,7 @@ bool ClassLinker::LinkInterfaceMethods(
self->AssertPendingOOMException();
return false;
}
- out = virtuals->Begin(method_size, method_alignment) + old_method_count;
+ out = virtuals->begin(method_size, method_alignment) + old_method_count;
size_t vtable_pos = old_vtable_count;
for (size_t i = old_method_count; i < new_method_count; ++i) {
// Leave the declaring class alone as type indices are relative to it
@@ -6387,7 +6377,6 @@ void ClassLinker::InsertDexFileInToClassLoader(mirror::Object* dex_file,
void ClassLinker::CleanupClassLoaders() {
Thread* const self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) {
const ClassLoaderData& data = *it;
// Need to use DecodeJObject so that we get null for cleared JNI weak globals.
@@ -6395,10 +6384,7 @@ void ClassLinker::CleanupClassLoaders() {
if (class_loader != nullptr) {
++it;
} else {
- // Weak reference was cleared, delete the data associated with this class loader.
- delete data.class_table;
- delete data.allocator;
- vm->DeleteWeakGlobalRef(self, data.weak_root);
+ DeleteClassLoader(self, data);
it = class_loaders_.erase(it);
}
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a2d38ac620..392efd23e2 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -551,6 +551,10 @@ class ClassLinker {
LinearAlloc* allocator;
};
+ static void DeleteClassLoader(Thread* self, const ClassLoaderData& data)
+ REQUIRES(Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
void VisitClassLoaders(ClassLoaderVisitor* visitor) const
SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 0926ce3f6a..04b890063d 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1032,9 +1032,7 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) {
mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
ArtMethod* clinit = klass->FindClassInitializer(sizeof(void*));
ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;", sizeof(void*));
- const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
- ASSERT_TRUE(string_id != nullptr);
- const DexFile::TypeId* type_id = dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id));
+ const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;");
ASSERT_TRUE(type_id != nullptr);
uint32_t type_idx = dex_file->GetIndexForTypeId(*type_id);
mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, soa.Self(), true, false);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index b17b76e2ea..7117be9a54 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -69,29 +69,26 @@ static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
return alloc_record_count;
}
-class Breakpoint {
+class Breakpoint : public ValueObject {
public:
- Breakpoint(ArtMethod* method, uint32_t dex_pc,
- DeoptimizationRequest::Kind deoptimization_kind)
- SHARED_REQUIRES(Locks::mutator_lock_)
- : method_(nullptr), dex_pc_(dex_pc), deoptimization_kind_(deoptimization_kind) {
+ Breakpoint(ArtMethod* method, uint32_t dex_pc, DeoptimizationRequest::Kind deoptimization_kind)
+ : method_(method),
+ dex_pc_(dex_pc),
+ deoptimization_kind_(deoptimization_kind) {
CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing ||
deoptimization_kind_ == DeoptimizationRequest::kSelectiveDeoptimization ||
deoptimization_kind_ == DeoptimizationRequest::kFullDeoptimization);
- ScopedObjectAccessUnchecked soa(Thread::Current());
- method_ = soa.EncodeMethod(method);
}
Breakpoint(const Breakpoint& other) SHARED_REQUIRES(Locks::mutator_lock_)
- : method_(nullptr), dex_pc_(other.dex_pc_),
- deoptimization_kind_(other.deoptimization_kind_) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- method_ = soa.EncodeMethod(other.Method());
- }
+ : method_(other.method_),
+ dex_pc_(other.dex_pc_),
+ deoptimization_kind_(other.deoptimization_kind_) {}
- ArtMethod* Method() const SHARED_REQUIRES(Locks::mutator_lock_) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- return soa.DecodeMethod(method_);
+ // Method() is called from root visiting, do not use ScopedObjectAccess here or it can cause
+ // GC to deadlock if another thread tries to call SuspendAll while the GC is in a runnable state.
+ ArtMethod* Method() const {
+ return method_;
}
uint32_t DexPc() const {
@@ -104,7 +101,7 @@ class Breakpoint {
private:
// The location of this breakpoint.
- jmethodID method_;
+ ArtMethod* method_;
uint32_t dex_pc_;
// Indicates whether breakpoint needs full deoptimization or selective deoptimization.
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index ae62e2bfe1..3a93aace83 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -37,6 +37,7 @@
#include "dex_file-inl.h"
#include "dex_file_verifier.h"
#include "globals.h"
+#include "handle_scope-inl.h"
#include "leb128.h"
#include "mirror/field.h"
#include "mirror/method.h"
@@ -44,8 +45,8 @@
#include "os.h"
#include "reflection.h"
#include "safe_map.h"
-#include "handle_scope-inl.h"
#include "thread.h"
+#include "type_lookup_table.h"
#include "utf-inl.h"
#include "utils.h"
#include "well_known_classes.h"
@@ -414,11 +415,19 @@ DexFile::DexFile(const uint8_t* base, size_t size,
method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
- find_class_def_misses_(0),
- class_def_index_(nullptr),
oat_dex_file_(oat_dex_file) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
+ const uint8_t* lookup_data = (oat_dex_file != nullptr)
+ ? oat_dex_file->GetLookupTableData()
+ : nullptr;
+ if (lookup_data != nullptr) {
+ if (lookup_data + TypeLookupTable::RawDataLength(*this) > oat_dex_file->GetOatFile()->End()) {
+ LOG(WARNING) << "found truncated lookup table in " << GetLocation();
+ } else {
+ lookup_table_.reset(TypeLookupTable::Open(lookup_data, *this));
+ }
+ }
}
DexFile::~DexFile() {
@@ -426,8 +435,6 @@ DexFile::~DexFile() {
// that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
// re-attach, but cleaning up these global references is not obviously useful. It's not as if
// the global reference table is otherwise empty!
- // Remove the index if one were created.
- delete class_def_index_.LoadRelaxed();
}
bool DexFile::Init(std::string* error_msg) {
@@ -477,50 +484,25 @@ uint32_t DexFile::GetVersion() const {
const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor, size_t hash) const {
DCHECK_EQ(ComputeModifiedUtf8Hash(descriptor), hash);
- // If we have an index lookup the descriptor via that as its constant time to search.
- Index* index = class_def_index_.LoadSequentiallyConsistent();
- if (index != nullptr) {
- auto it = index->FindWithHash(descriptor, hash);
- return (it == index->end()) ? nullptr : it->second;
+ if (LIKELY(lookup_table_ != nullptr)) {
+ const uint32_t class_def_idx = lookup_table_->Lookup(descriptor, hash);
+ return (class_def_idx != DexFile::kDexNoIndex) ? &GetClassDef(class_def_idx) : nullptr;
}
+
// Fast path for rate no class defs case.
- uint32_t num_class_defs = NumClassDefs();
+ const uint32_t num_class_defs = NumClassDefs();
if (num_class_defs == 0) {
return nullptr;
}
- // Search for class def with 2 binary searches and then a linear search.
- const StringId* string_id = FindStringId(descriptor);
- if (string_id != nullptr) {
- const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
- if (type_id != nullptr) {
- uint16_t type_idx = GetIndexForTypeId(*type_id);
- for (size_t i = 0; i < num_class_defs; ++i) {
- const ClassDef& class_def = GetClassDef(i);
- if (class_def.class_idx_ == type_idx) {
- return &class_def;
- }
- }
- }
- }
- // A miss. If we've had kMaxFailedDexClassDefLookups misses then build an index to speed things
- // up. This isn't done eagerly at construction as construction is not performed in multi-threaded
- // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
- // out which thread builds the index.
- const uint32_t kMaxFailedDexClassDefLookups = 100;
- uint32_t old_misses = find_class_def_misses_.FetchAndAddSequentiallyConsistent(1);
- if (old_misses == kMaxFailedDexClassDefLookups) {
- // Are we the ones moving the miss count past the max? Sanity check the index doesn't exist.
- CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
- // Build the index.
- index = new Index();
- for (uint32_t i = 0; i < num_class_defs; ++i) {
+ const TypeId* type_id = FindTypeId(descriptor);
+ if (type_id != nullptr) {
+ uint16_t type_idx = GetIndexForTypeId(*type_id);
+ for (size_t i = 0; i < num_class_defs; ++i) {
const ClassDef& class_def = GetClassDef(i);
- const char* class_descriptor = GetClassDescriptor(class_def);
- index->Insert(std::make_pair(class_descriptor, &class_def));
+ if (class_def.class_idx_ == type_idx) {
+ return &class_def;
+ }
}
- // Sanity check the index still doesn't exist, only 1 thread should build it.
- CHECK(class_def_index_.LoadSequentiallyConsistent() == nullptr);
- class_def_index_.StoreSequentiallyConsistent(index);
}
return nullptr;
}
@@ -625,6 +607,26 @@ const DexFile::StringId* DexFile::FindStringId(const char* string) const {
return nullptr;
}
+const DexFile::TypeId* DexFile::FindTypeId(const char* string) const {
+ int32_t lo = 0;
+ int32_t hi = NumTypeIds() - 1;
+ while (hi >= lo) {
+ int32_t mid = (hi + lo) / 2;
+ const TypeId& type_id = GetTypeId(mid);
+ const DexFile::StringId& str_id = GetStringId(type_id.descriptor_idx_);
+ const char* str = GetStringData(str_id);
+ int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
+ if (compare > 0) {
+ lo = mid + 1;
+ } else if (compare < 0) {
+ hi = mid - 1;
+ } else {
+ return &type_id;
+ }
+ }
+ return nullptr;
+}
+
const DexFile::StringId* DexFile::FindStringId(const uint16_t* string, size_t length) const {
int32_t lo = 0;
int32_t hi = NumStringIds() - 1;
@@ -697,6 +699,10 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx,
return nullptr;
}
+void DexFile::CreateTypeLookupTable() const {
+ lookup_table_.reset(TypeLookupTable::Create(*this));
+}
+
// Given a signature place the type ids into the given vector
bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
std::vector<uint16_t>* param_type_idxs) const {
@@ -732,11 +738,7 @@ bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type
}
// TODO: avoid creating a std::string just to get a 0-terminated char array
std::string descriptor(signature.data() + start_offset, offset - start_offset);
- const DexFile::StringId* string_id = FindStringId(descriptor.c_str());
- if (string_id == nullptr) {
- return false;
- }
- const DexFile::TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
+ const DexFile::TypeId* type_id = FindTypeId(descriptor.c_str());
if (type_id == nullptr) {
return false;
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 47e5c124ff..e7877b2e78 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -51,6 +51,7 @@ class OatDexFile;
class Signature;
template<class T> class Handle;
class StringPiece;
+class TypeLookupTable;
class ZipArchive;
// TODO: move all of the macro functionality into the DexCache class.
@@ -532,6 +533,8 @@ class DexFile {
// Looks up a string id for a given modified utf8 string.
const StringId* FindStringId(const char* string) const;
+ const TypeId* FindTypeId(const char* string) const;
+
// Looks up a string id for a given utf16 string.
const StringId* FindStringId(const uint16_t* string, size_t length) const;
@@ -1139,6 +1142,12 @@ class DexFile {
return oat_dex_file_;
}
+ TypeLookupTable* GetTypeLookupTable() const {
+ return lookup_table_.get();
+ }
+
+ void CreateTypeLookupTable() const;
+
private:
// Opens a .dex file
static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location,
@@ -1237,44 +1246,11 @@ class DexFile {
// Points to the base of the class definition list.
const ClassDef* const class_defs_;
- // Number of misses finding a class def from a descriptor.
- mutable Atomic<uint32_t> find_class_def_misses_;
-
- struct UTF16EmptyFn {
- void MakeEmpty(std::pair<const char*, const ClassDef*>& pair) const {
- pair.first = nullptr;
- pair.second = nullptr;
- }
- bool IsEmpty(const std::pair<const char*, const ClassDef*>& pair) const {
- if (pair.first == nullptr) {
- DCHECK(pair.second == nullptr);
- return true;
- }
- return false;
- }
- };
- struct UTF16HashCmp {
- // Hash function.
- size_t operator()(const char* key) const {
- return ComputeModifiedUtf8Hash(key);
- }
- // std::equal function.
- bool operator()(const char* a, const char* b) const {
- return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0;
- }
- };
- using Index = HashMap<const char*,
- const ClassDef*,
- UTF16EmptyFn,
- UTF16HashCmp,
- UTF16HashCmp,
- std::allocator<std::pair<const char*, const ClassDef*>>>;
- mutable Atomic<Index*> class_def_index_;
-
// If this dex file was loaded from an oat file, oat_dex_file_ contains a
// pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is
// null.
const OatDexFile* oat_dex_file_;
+ mutable std::unique_ptr<TypeLookupTable> lookup_table_;
friend class DexFileVerifierTest;
};
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 90b35a3e04..0a167bb8f7 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -297,6 +297,7 @@ TEST_F(DexFileTest, FindTypeId) {
ASSERT_TRUE(type_str_id != nullptr);
uint32_t type_str_idx = java_lang_dex_file_->GetIndexForStringId(*type_str_id);
const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(type_str_idx);
+ ASSERT_EQ(type_id, java_lang_dex_file_->FindTypeId(type_str));
ASSERT_TRUE(type_id != nullptr);
EXPECT_EQ(java_lang_dex_file_->GetIndexForTypeId(*type_id), i);
}
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index a5f9d09900..440d696ea9 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -1416,7 +1416,12 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c
}
if (IsDataSectionType(type)) {
- offset_to_type_map_.Put(aligned_offset, type);
+ if (aligned_offset == 0u) {
+ ErrorStringPrintf("Item %d offset is 0", i);
+ return false;
+ }
+ DCHECK(offset_to_type_map_.Find(aligned_offset) == offset_to_type_map_.end());
+ offset_to_type_map_.Insert(std::pair<uint32_t, uint16_t>(aligned_offset, type));
}
aligned_offset = ptr_ - begin_;
@@ -1589,7 +1594,8 @@ bool DexFileVerifier::CheckIntraSection() {
}
bool DexFileVerifier::CheckOffsetToTypeMap(size_t offset, uint16_t type) {
- auto it = offset_to_type_map_.find(offset);
+ DCHECK_NE(offset, 0u);
+ auto it = offset_to_type_map_.Find(offset);
if (UNLIKELY(it == offset_to_type_map_.end())) {
ErrorStringPrintf("No data map entry found @ %zx; expected %x", offset, type);
return false;
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 4f15357ea0..6c63749f04 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -175,7 +175,35 @@ class DexFileVerifier {
const char* const location_;
const DexFile::Header* const header_;
- AllocationTrackingSafeMap<uint32_t, uint16_t, kAllocatorTagDexFileVerifier> offset_to_type_map_;
+ struct OffsetTypeMapEmptyFn {
+ // Make a hash map slot empty by making the offset 0. Offset 0 is a valid dex file offset that
+ // is in the offset of the dex file header. However, we only store data section items in the
+ // map, and these are after the header.
+ void MakeEmpty(std::pair<uint32_t, uint16_t>& pair) const {
+ pair.first = 0u;
+ }
+ // Check if a hash map slot is empty.
+ bool IsEmpty(const std::pair<uint32_t, uint16_t>& pair) const {
+ return pair.first == 0;
+ }
+ };
+ struct OffsetTypeMapHashCompareFn {
+ // Hash function for offset.
+ size_t operator()(const uint32_t key) const {
+ return key;
+ }
+ // std::equal function for offset.
+ bool operator()(const uint32_t a, const uint32_t b) const {
+ return a == b;
+ }
+ };
+ // Map from offset to dex file type, HashMap for performance reasons.
+ AllocationTrackingHashMap<uint32_t,
+ uint16_t,
+ OffsetTypeMapEmptyFn,
+ kAllocatorTagDexFileVerifier,
+ OffsetTypeMapHashCompareFn,
+ OffsetTypeMapHashCompareFn> offset_to_type_map_;
const uint8_t* ptr_;
const void* previous_item_;
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 2871f76114..035230eb8c 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -185,6 +185,7 @@ class Instruction {
static constexpr uint32_t kMaxVarArgRegs = 5;
static constexpr uint32_t kMaxVarArgRegs25x = 6; // lambdas are 2 registers.
+ static constexpr uint32_t kLambdaVirtualRegisterWidth = 2;
// Returns the size (in 2 byte code units) of this instruction.
size_t SizeInCodeUnits() const {
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 4de8a8ead9..18ccd082ec 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -92,10 +92,25 @@ class ExceptionTest : public CommonRuntimeTest {
fake_header_code_and_maps_.insert(fake_header_code_and_maps_.end(),
fake_code_.begin(), fake_code_.end());
- // NOTE: Don't align the code (it will not be executed) but check that the Thumb2
- // adjustment will be a NOP, see ArtMethod::EntryPointToCodePointer().
- CHECK_ALIGNED(mapping_table_offset, 2);
- const uint8_t* code_ptr = &fake_header_code_and_maps_[gc_map_offset];
+ // Align the code.
+ const size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ fake_header_code_and_maps_.reserve(fake_header_code_and_maps_.size() + alignment);
+ const void* unaligned_code_ptr =
+ fake_header_code_and_maps_.data() + (fake_header_code_and_maps_.size() - code_size);
+ size_t offset = dchecked_integral_cast<size_t>(reinterpret_cast<uintptr_t>(unaligned_code_ptr));
+ size_t padding = RoundUp(offset, alignment) - offset;
+ // Make sure no resizing takes place.
+ CHECK_GE(fake_header_code_and_maps_.capacity(), fake_header_code_and_maps_.size() + padding);
+ fake_header_code_and_maps_.insert(fake_header_code_and_maps_.begin(), padding, 0);
+ const void* code_ptr = reinterpret_cast<const uint8_t*>(unaligned_code_ptr) + padding;
+ CHECK_EQ(code_ptr,
+ static_cast<const void*>(fake_header_code_and_maps_.data() +
+ (fake_header_code_and_maps_.size() - code_size)));
+
+ if (kRuntimeISA == kArm) {
+ // Check that the Thumb2 adjustment will be a NOP, see EntryPointToCodePointer().
+ CHECK_ALIGNED(mapping_table_offset, 2);
+ }
method_f_ = my_klass_->FindVirtualMethod("f", "()I", sizeof(void*));
ASSERT_TRUE(method_f_ != nullptr);
diff --git a/runtime/gc/accounting/bitmap.cc b/runtime/gc/accounting/bitmap.cc
index fdded028e1..380cb8efed 100644
--- a/runtime/gc/accounting/bitmap.cc
+++ b/runtime/gc/accounting/bitmap.cc
@@ -18,6 +18,7 @@
#include "base/bit_utils.h"
#include "card_table.h"
+#include "jit/jit_code_cache.h"
#include "mem_map.h"
namespace art {
@@ -91,6 +92,7 @@ MemoryRangeBitmap<kAlignment>* MemoryRangeBitmap<kAlignment>::CreateFromMemMap(
}
template class MemoryRangeBitmap<CardTable::kCardSize>;
+template class MemoryRangeBitmap<jit::kJitCodeAlignment>;
} // namespace accounting
} // namespace gc
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index 006d2c7d30..3be7181df2 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -46,7 +46,7 @@ inline bool SpaceBitmap<kAlignment>::AtomicTestAndSet(const mirror::Object* obj)
DCHECK(Test(obj));
return true;
}
- } while (!atomic_entry->CompareExchangeWeakSequentiallyConsistent(old_word, old_word | mask));
+ } while (!atomic_entry->CompareExchangeWeakRelaxed(old_word, old_word | mask));
DCHECK(Test(obj));
return false;
}
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
new file mode 100644
index 0000000000..26f5ad3df5
--- /dev/null
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
+#define ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
+
+#include "concurrent_copying.h"
+
+#include "gc/accounting/space_bitmap-inl.h"
+#include "gc/heap.h"
+#include "gc/space/region_space.h"
+#include "lock_word.h"
+
+namespace art {
+namespace gc {
+namespace collector {
+
+inline mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
+ if (from_ref == nullptr) {
+ return nullptr;
+ }
+ DCHECK(heap_->collector_type_ == kCollectorTypeCC);
+ if (UNLIKELY(kUseBakerReadBarrier && !is_active_)) {
+ // In the lock word forward address state, the read barrier bits
+ // in the lock word are part of the stored forwarding address and
+ // invalid. This is usually OK as the from-space copy of objects
+ // aren't accessed by mutators due to the to-space
+ // invariant. However, during the dex2oat image writing relocation
+ // and the zygote compaction, objects can be in the forward
+ // address state (to store the forward/relocation addresses) and
+ // they can still be accessed and the invalid read barrier bits
+ // are consulted. If they look like gray but aren't really, the
+ // read barriers slow path can trigger when it shouldn't. To guard
+ // against this, return here if the CC collector isn't running.
+ return from_ref;
+ }
+ DCHECK(region_space_ != nullptr) << "Read barrier slow path taken when CC isn't running?";
+ space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
+ switch (rtype) {
+ case space::RegionSpace::RegionType::kRegionTypeToSpace:
+ // It's already marked.
+ return from_ref;
+ case space::RegionSpace::RegionType::kRegionTypeFromSpace: {
+ mirror::Object* to_ref = GetFwdPtr(from_ref);
+ if (kUseBakerReadBarrier) {
+ DCHECK_NE(to_ref, ReadBarrier::GrayPtr())
+ << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ }
+ if (to_ref == nullptr) {
+ // It isn't marked yet. Mark it by copying it to the to-space.
+ to_ref = Copy(from_ref);
+ }
+ DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
+ << "from_ref=" << from_ref << " to_ref=" << to_ref;
+ return to_ref;
+ }
+ case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace: {
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ mirror::Object* to_ref = from_ref;
+ if (region_space_bitmap_->AtomicTestAndSet(from_ref)) {
+ // Already marked.
+ } else {
+ // Newly marked.
+ if (kUseBakerReadBarrier) {
+ DCHECK_EQ(to_ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
+ }
+ PushOntoMarkStack(to_ref);
+ }
+ return to_ref;
+ }
+ case space::RegionSpace::RegionType::kRegionTypeNone:
+ return MarkNonMoving(from_ref);
+ default:
+ UNREACHABLE();
+ }
+}
+
+inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
+ DCHECK(region_space_->IsInFromSpace(from_ref));
+ LockWord lw = from_ref->GetLockWord(false);
+ if (lw.GetState() == LockWord::kForwardingAddress) {
+ mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
+ DCHECK(fwd_ptr != nullptr);
+ return fwd_ptr;
+ } else {
+ return nullptr;
+ }
+}
+
+} // namespace collector
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_COLLECTOR_CONCURRENT_COPYING_INL_H_
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index d2d12af6b4..4a49712cbc 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -36,13 +36,16 @@ namespace art {
namespace gc {
namespace collector {
+static constexpr size_t kDefaultGcMarkStackSize = 2 * MB;
+
ConcurrentCopying::ConcurrentCopying(Heap* heap, const std::string& name_prefix)
: GarbageCollector(heap,
name_prefix + (name_prefix.empty() ? "" : " ") +
"concurrent copying + mark sweep"),
region_space_(nullptr), gc_barrier_(new Barrier(0)),
gc_mark_stack_(accounting::ObjectStack::Create("concurrent copying gc mark stack",
- 2 * MB, 2 * MB)),
+ kDefaultGcMarkStackSize,
+ kDefaultGcMarkStackSize)),
mark_stack_lock_("concurrent copying mark stack lock", kMarkSweepMarkStackLock),
thread_running_gc_(nullptr),
is_marking_(false), is_active_(false), is_asserting_to_space_invariant_(false),
@@ -336,9 +339,7 @@ class EmptyCheckpoint : public Closure {
<< thread->GetState() << " thread " << thread << " self " << self;
// If thread is a running mutator, then act on behalf of the garbage collector.
// See the code in ThreadList::RunCheckpoint.
- if (thread->GetState() == kRunnable) {
- concurrent_copying_->GetBarrier().Pass(self);
- }
+ concurrent_copying_->GetBarrier().Pass(self);
}
private:
@@ -367,30 +368,15 @@ void ConcurrentCopying::MarkingPhase() {
}
}
}
- // TODO: Other garbage collectors uses Runtime::VisitConcurrentRoots(), refactor this part
- // to also use the same function.
- {
- TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
- Runtime::Current()->VisitConstantRoots(this);
- }
- {
- TimingLogger::ScopedTiming split3("VisitInternTableRoots", GetTimings());
- Runtime::Current()->GetInternTable()->VisitRoots(this, kVisitRootFlagAllRoots);
- }
{
- TimingLogger::ScopedTiming split4("VisitClassLinkerRoots", GetTimings());
- Runtime::Current()->GetClassLinker()->VisitRoots(this, kVisitRootFlagAllRoots);
+ TimingLogger::ScopedTiming split2("VisitConcurrentRoots", GetTimings());
+ Runtime::Current()->VisitConcurrentRoots(this, kVisitRootFlagAllRoots);
}
{
// TODO: don't visit the transaction roots if it's not active.
TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
Runtime::Current()->VisitNonThreadRoots(this);
}
- {
- TimingLogger::ScopedTiming split6("Dbg::VisitRoots", GetTimings());
- Dbg::VisitRoots(this);
- }
- Runtime::Current()->GetHeap()->VisitAllocationRecords(this);
// Immune spaces.
for (auto& space : heap_->GetContinuousSpaces()) {
@@ -511,9 +497,7 @@ class DisableMarkingCheckpoint : public Closure {
thread->SetIsGcMarking(false);
// If thread is a running mutator, then act on behalf of the garbage collector.
// See the code in ThreadList::RunCheckpoint.
- if (thread->GetState() == kRunnable) {
- concurrent_copying_->GetBarrier().Pass(self);
- }
+ concurrent_copying_->GetBarrier().Pass(self);
}
private:
@@ -577,17 +561,31 @@ void ConcurrentCopying::IssueEmptyCheckpoint() {
Locks::mutator_lock_->SharedLock(self);
}
+void ConcurrentCopying::ExpandGcMarkStack() {
+ DCHECK(gc_mark_stack_->IsFull());
+ const size_t new_size = gc_mark_stack_->Capacity() * 2;
+ std::vector<StackReference<mirror::Object>> temp(gc_mark_stack_->Begin(),
+ gc_mark_stack_->End());
+ gc_mark_stack_->Resize(new_size);
+ for (auto& ref : temp) {
+ gc_mark_stack_->PushBack(ref.AsMirrorPtr());
+ }
+ DCHECK(!gc_mark_stack_->IsFull());
+}
+
void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
CHECK_EQ(is_mark_stack_push_disallowed_.LoadRelaxed(), 0)
<< " " << to_ref << " " << PrettyTypeOf(to_ref);
Thread* self = Thread::Current(); // TODO: pass self as an argument from call sites?
CHECK(thread_running_gc_ != nullptr);
MarkStackMode mark_stack_mode = mark_stack_mode_.LoadRelaxed();
- if (mark_stack_mode == kMarkStackModeThreadLocal) {
- if (self == thread_running_gc_) {
+ if (LIKELY(mark_stack_mode == kMarkStackModeThreadLocal)) {
+ if (LIKELY(self == thread_running_gc_)) {
// If GC-running thread, use the GC mark stack instead of a thread-local mark stack.
CHECK(self->GetThreadLocalMarkStack() == nullptr);
- CHECK(!gc_mark_stack_->IsFull());
+ if (UNLIKELY(gc_mark_stack_->IsFull())) {
+ ExpandGcMarkStack();
+ }
gc_mark_stack_->PushBack(to_ref);
} else {
// Otherwise, use a thread-local mark stack.
@@ -621,7 +619,9 @@ void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
} else if (mark_stack_mode == kMarkStackModeShared) {
// Access the shared GC mark stack with a lock.
MutexLock mu(self, mark_stack_lock_);
- CHECK(!gc_mark_stack_->IsFull());
+ if (UNLIKELY(gc_mark_stack_->IsFull())) {
+ ExpandGcMarkStack();
+ }
gc_mark_stack_->PushBack(to_ref);
} else {
CHECK_EQ(static_cast<uint32_t>(mark_stack_mode),
@@ -633,7 +633,9 @@ void ConcurrentCopying::PushOntoMarkStack(mirror::Object* to_ref) {
<< "Only GC-running thread should access the mark stack "
<< "in the GC exclusive mark stack mode";
// Access the GC mark stack without a lock.
- CHECK(!gc_mark_stack_->IsFull());
+ if (UNLIKELY(gc_mark_stack_->IsFull())) {
+ ExpandGcMarkStack();
+ }
gc_mark_stack_->PushBack(to_ref);
}
}
@@ -646,18 +648,6 @@ accounting::ObjectStack* ConcurrentCopying::GetLiveStack() {
return heap_->live_stack_.get();
}
-inline mirror::Object* ConcurrentCopying::GetFwdPtr(mirror::Object* from_ref) {
- DCHECK(region_space_->IsInFromSpace(from_ref));
- LockWord lw = from_ref->GetLockWord(false);
- if (lw.GetState() == LockWord::kForwardingAddress) {
- mirror::Object* fwd_ptr = reinterpret_cast<mirror::Object*>(lw.ForwardingAddress());
- CHECK(fwd_ptr != nullptr);
- return fwd_ptr;
- } else {
- return nullptr;
- }
-}
-
// The following visitors are that used to verify that there's no
// references to the from-space left after marking.
class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor : public SingleRootVisitor {
@@ -916,9 +906,7 @@ class RevokeThreadLocalMarkStackCheckpoint : public Closure {
}
// If thread is a running mutator, then act on behalf of the garbage collector.
// See the code in ThreadList::RunCheckpoint.
- if (thread->GetState() == kRunnable) {
- concurrent_copying_->GetBarrier().Pass(self);
- }
+ concurrent_copying_->GetBarrier().Pass(self);
}
private:
@@ -1065,7 +1053,7 @@ size_t ConcurrentCopying::ProcessThreadLocalMarkStacks(bool disable_weak_ref_acc
return count;
}
-void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
+inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
if (kUseBakerReadBarrier) {
DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr())
@@ -1080,9 +1068,10 @@ void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
<< " " << to_ref << " " << to_ref->GetReadBarrierPointer()
<< " is_marked=" << IsMarked(to_ref);
}
- if (to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
- to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
- !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())) {
+#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
+ if (UNLIKELY((to_ref->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass() &&
+ to_ref->AsReference()->GetReferent<kWithoutReadBarrier>() != nullptr &&
+ !IsInToSpace(to_ref->AsReference()->GetReferent<kWithoutReadBarrier>())))) {
// Leave this Reference gray in the queue so that GetReferent() will trigger a read barrier. We
// will change it to black or white later in ReferenceQueue::DequeuePendingReference().
CHECK(to_ref->AsReference()->IsEnqueued()) << "Left unenqueued ref gray " << to_ref;
@@ -1091,14 +1080,13 @@ void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
// be concurrently marked after the Scan() call above has enqueued the Reference, in which case
// the above IsInToSpace() evaluates to true and we change the color from gray to black or white
// here in this else block.
-#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
if (kUseBakerReadBarrier) {
if (region_space_->IsInToSpace(to_ref)) {
// If to-space, change from gray to white.
bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
ReadBarrier::WhitePtr());
CHECK(success) << "Must succeed as we won the race.";
- CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
} else {
// If non-moving space/unevac from space, change from gray
// to black. We can't change gray to white because it's not
@@ -1110,13 +1098,13 @@ void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
bool success = to_ref->AtomicSetReadBarrierPointer(ReadBarrier::GrayPtr(),
ReadBarrier::BlackPtr());
CHECK(success) << "Must succeed as we won the race.";
- CHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
}
}
+ }
#else
- DCHECK(!kUseBakerReadBarrier);
+ DCHECK(!kUseBakerReadBarrier);
#endif
- }
if (ReadBarrier::kEnableToSpaceInvariantChecks || kIsDebugBuild) {
ConcurrentCopyingAssertToSpaceInvariantObjectVisitor visitor(this);
visitor(to_ref);
@@ -1607,6 +1595,7 @@ class ConcurrentCopyingRefFieldsVisitor {
}
void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
SHARED_REQUIRES(Locks::mutator_lock_) {
if (!root->IsNull()) {
VisitRoot(root);
@@ -1614,6 +1603,7 @@ class ConcurrentCopyingRefFieldsVisitor {
}
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
SHARED_REQUIRES(Locks::mutator_lock_) {
collector_->MarkRoot(root);
}
@@ -1623,7 +1613,7 @@ class ConcurrentCopyingRefFieldsVisitor {
};
// Scan ref fields of an object.
-void ConcurrentCopying::Scan(mirror::Object* to_ref) {
+inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
ConcurrentCopyingRefFieldsVisitor visitor(this);
to_ref->VisitReferences(visitor, visitor);
@@ -1633,9 +1623,6 @@ void ConcurrentCopying::Scan(mirror::Object* to_ref) {
inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset) {
mirror::Object* ref = obj->GetFieldObject<
mirror::Object, kVerifyNone, kWithoutReadBarrier, false>(offset);
- if (ref == nullptr || region_space_->IsInToSpace(ref)) {
- return;
- }
mirror::Object* to_ref = Mark(ref);
if (to_ref == ref) {
return;
@@ -1649,19 +1636,16 @@ inline void ConcurrentCopying::Process(mirror::Object* obj, MemberOffset offset)
// It was updated by the mutator.
break;
}
- } while (!obj->CasFieldWeakSequentiallyConsistentObjectWithoutWriteBarrier<
+ } while (!obj->CasFieldWeakRelaxedObjectWithoutWriteBarrier<
false, false, kVerifyNone>(offset, expected_ref, new_ref));
}
// Process some roots.
-void ConcurrentCopying::VisitRoots(
+inline void ConcurrentCopying::VisitRoots(
mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) {
for (size_t i = 0; i < count; ++i) {
mirror::Object** root = roots[i];
mirror::Object* ref = *root;
- if (ref == nullptr || region_space_->IsInToSpace(ref)) {
- continue;
- }
mirror::Object* to_ref = Mark(ref);
if (to_ref == ref) {
continue;
@@ -1674,16 +1658,13 @@ void ConcurrentCopying::VisitRoots(
// It was updated by the mutator.
break;
}
- } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+ } while (!addr->CompareExchangeWeakRelaxed(expected_ref, new_ref));
}
}
-void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
+inline void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* root) {
DCHECK(!root->IsNull());
mirror::Object* const ref = root->AsMirrorPtr();
- if (region_space_->IsInToSpace(ref)) {
- return;
- }
mirror::Object* to_ref = Mark(ref);
if (to_ref != ref) {
auto* addr = reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root);
@@ -1695,11 +1676,11 @@ void ConcurrentCopying::MarkRoot(mirror::CompressedReference<mirror::Object>* ro
// It was updated by the mutator.
break;
}
- } while (!addr->CompareExchangeWeakSequentiallyConsistent(expected_ref, new_ref));
+ } while (!addr->CompareExchangeWeakRelaxed(expected_ref, new_ref));
}
}
-void ConcurrentCopying::VisitRoots(
+inline void ConcurrentCopying::VisitRoots(
mirror::CompressedReference<mirror::Object>** roots, size_t count,
const RootInfo& info ATTRIBUTE_UNUSED) {
for (size_t i = 0; i < count; ++i) {
@@ -1998,148 +1979,85 @@ bool ConcurrentCopying::IsOnAllocStack(mirror::Object* ref) {
return alloc_stack->Contains(ref);
}
-mirror::Object* ConcurrentCopying::Mark(mirror::Object* from_ref) {
- if (from_ref == nullptr) {
- return nullptr;
- }
- DCHECK(from_ref != nullptr);
- DCHECK(heap_->collector_type_ == kCollectorTypeCC);
- if (kUseBakerReadBarrier && !is_active_) {
- // In the lock word forward address state, the read barrier bits
- // in the lock word are part of the stored forwarding address and
- // invalid. This is usually OK as the from-space copy of objects
- // aren't accessed by mutators due to the to-space
- // invariant. However, during the dex2oat image writing relocation
- // and the zygote compaction, objects can be in the forward
- // address state (to store the forward/relocation addresses) and
- // they can still be accessed and the invalid read barrier bits
- // are consulted. If they look like gray but aren't really, the
- // read barriers slow path can trigger when it shouldn't. To guard
- // against this, return here if the CC collector isn't running.
- return from_ref;
- }
- DCHECK(region_space_ != nullptr) << "Read barrier slow path taken when CC isn't running?";
- space::RegionSpace::RegionType rtype = region_space_->GetRegionType(from_ref);
- if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
- // It's already marked.
- return from_ref;
- }
- mirror::Object* to_ref;
- if (rtype == space::RegionSpace::RegionType::kRegionTypeFromSpace) {
- to_ref = GetFwdPtr(from_ref);
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref != ReadBarrier::GrayPtr()) << "from_ref=" << from_ref << " to_ref=" << to_ref;
- }
- if (to_ref == nullptr) {
- // It isn't marked yet. Mark it by copying it to the to-space.
- to_ref = Copy(from_ref);
+mirror::Object* ConcurrentCopying::MarkNonMoving(mirror::Object* ref) {
+ // ref is in a non-moving space (from_ref == to_ref).
+ DCHECK(!region_space_->HasAddress(ref)) << ref;
+ if (immune_region_.ContainsObject(ref)) {
+ accounting::ContinuousSpaceBitmap* cc_bitmap =
+ cc_heap_bitmap_->GetContinuousSpaceBitmap(ref);
+ DCHECK(cc_bitmap != nullptr)
+ << "An immune space object must have a bitmap";
+ if (kIsDebugBuild) {
+ DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(ref)->Test(ref))
+ << "Immune space object must be already marked";
}
- DCHECK(region_space_->IsInToSpace(to_ref) || heap_->non_moving_space_->HasAddress(to_ref))
- << "from_ref=" << from_ref << " to_ref=" << to_ref;
- } else if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
// This may or may not succeed, which is ok.
if (kUseBakerReadBarrier) {
- from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
}
- if (region_space_bitmap_->AtomicTestAndSet(from_ref)) {
+ if (cc_bitmap->AtomicTestAndSet(ref)) {
// Already marked.
- to_ref = from_ref;
} else {
// Newly marked.
- to_ref = from_ref;
if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
}
- PushOntoMarkStack(to_ref);
+ PushOntoMarkStack(ref);
}
} else {
- // from_ref is in a non-moving space.
- DCHECK(!region_space_->HasAddress(from_ref)) << from_ref;
- if (immune_region_.ContainsObject(from_ref)) {
- accounting::ContinuousSpaceBitmap* cc_bitmap =
- cc_heap_bitmap_->GetContinuousSpaceBitmap(from_ref);
- DCHECK(cc_bitmap != nullptr)
- << "An immune space object must have a bitmap";
- if (kIsDebugBuild) {
- DCHECK(heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref)->Test(from_ref))
- << "Immune space object must be already marked";
- }
- // This may or may not succeed, which is ok.
+ // Use the mark bitmap.
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(ref);
+ CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
+ bool is_los = mark_bitmap == nullptr;
+ if (!is_los && mark_bitmap->Test(ref)) {
+ // Already marked.
if (kUseBakerReadBarrier) {
- from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
}
- if (cc_bitmap->AtomicTestAndSet(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- } else {
- // Newly marked.
- to_ref = from_ref;
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
- }
- PushOntoMarkStack(to_ref);
+ } else if (is_los && los_bitmap->Test(ref)) {
+ // Already marked in LOS.
+ if (kUseBakerReadBarrier) {
+ DCHECK(ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
+ ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
}
} else {
- // Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
- bool is_los = mark_bitmap == nullptr;
- if (!is_los && mark_bitmap->Test(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
- to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ // Not marked.
+ if (IsOnAllocStack(ref)) {
+ // If it's on the allocation stack, it's considered marked. Keep it white.
+ // Objects on the allocation stack need not be marked.
+ if (!is_los) {
+ DCHECK(!mark_bitmap->Test(ref));
+ } else {
+ DCHECK(!los_bitmap->Test(ref));
}
- } else if (is_los && los_bitmap->Test(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr() ||
- to_ref->GetReadBarrierPointer() == ReadBarrier::BlackPtr());
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::WhitePtr());
}
} else {
- // Not marked.
- if (IsOnAllocStack(from_ref)) {
- // If it's on the allocation stack, it's considered marked. Keep it white.
- to_ref = from_ref;
- // Objects on the allocation stack need not be marked.
- if (!is_los) {
- DCHECK(!mark_bitmap->Test(to_ref));
- } else {
- DCHECK(!los_bitmap->Test(to_ref));
- }
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::WhitePtr());
- }
+ // Not marked or on the allocation stack. Try to mark it.
+ // This may or may not succeed, which is ok.
+ if (kUseBakerReadBarrier) {
+ ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
+ }
+ if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
+ // Already marked.
+ } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
+ // Already marked in LOS.
} else {
- // Not marked or on the allocation stack. Try to mark it.
- // This may or may not succeed, which is ok.
+ // Newly marked.
if (kUseBakerReadBarrier) {
- from_ref->AtomicSetReadBarrierPointer(ReadBarrier::WhitePtr(), ReadBarrier::GrayPtr());
- }
- if (!is_los && mark_bitmap->AtomicTestAndSet(from_ref)) {
- // Already marked.
- to_ref = from_ref;
- } else if (is_los && los_bitmap->AtomicTestAndSet(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
- } else {
- // Newly marked.
- to_ref = from_ref;
- if (kUseBakerReadBarrier) {
- DCHECK(to_ref->GetReadBarrierPointer() == ReadBarrier::GrayPtr());
- }
- PushOntoMarkStack(to_ref);
+ DCHECK_EQ(ref->GetReadBarrierPointer(), ReadBarrier::GrayPtr());
}
+ PushOntoMarkStack(ref);
}
}
}
}
- return to_ref;
+ return ref;
}
void ConcurrentCopying::FinishPhase() {
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 8efad731b8..27726e23c1 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -93,7 +93,7 @@ class ConcurrentCopying : public GarbageCollector {
DCHECK(ref != nullptr);
return IsMarked(ref) == ref;
}
- mirror::Object* Mark(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
+ ALWAYS_INLINE mirror::Object* Mark(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
bool IsMarking() const {
return is_marking_;
@@ -182,6 +182,9 @@ class ConcurrentCopying : public GarbageCollector {
void ReenableWeakRefAccess(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_);
void DisableMarking() SHARED_REQUIRES(Locks::mutator_lock_);
void IssueDisableMarkingCheckpoint() SHARED_REQUIRES(Locks::mutator_lock_);
+ void ExpandGcMarkStack() SHARED_REQUIRES(Locks::mutator_lock_);
+ mirror::Object* MarkNonMoving(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_);
space::RegionSpace* region_space_; // The underlying region space.
std::unique_ptr<Barrier> gc_barrier_;
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 77a288ba68..db516a0a87 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -1146,9 +1146,7 @@ class CheckpointMarkThreadRoots : public Closure, public RootVisitor {
}
// If thread is a running mutator, then act on behalf of the garbage collector.
// See the code in ThreadList::RunCheckpoint.
- if (thread->GetState() == kRunnable) {
- mark_sweep_->GetBarrier().Pass(self);
- }
+ mark_sweep_->GetBarrier().Pass(self);
}
private:
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 657fcb5f08..ab931429d0 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -26,6 +26,7 @@
#include "art_field-inl.h"
#include "base/allocator.h"
+#include "base/arena_allocator.h"
#include "base/dumpable.h"
#include "base/histogram-inl.h"
#include "base/stl_util.h"
@@ -1258,11 +1259,11 @@ void Heap::DoPendingCollectorTransition() {
}
void Heap::Trim(Thread* self) {
+ Runtime* const runtime = Runtime::Current();
if (!CareAboutPauseTimes()) {
ATRACE_BEGIN("Deflating monitors");
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
- Runtime* runtime = Runtime::Current();
{
ScopedSuspendAll ssa(__FUNCTION__);
uint64_t start_time = NanoTime();
@@ -1274,6 +1275,10 @@ void Heap::Trim(Thread* self) {
}
TrimIndirectReferenceTables(self);
TrimSpaces(self);
+ // Trim arenas that may have been used by JIT or verifier.
+ ATRACE_BEGIN("Trimming arena maps");
+ runtime->GetArenaPool()->TrimMaps();
+ ATRACE_END();
}
class TrimIndirectReferenceTableClosure : public Closure {
@@ -1286,9 +1291,7 @@ class TrimIndirectReferenceTableClosure : public Closure {
ATRACE_END();
// If thread is a running mutator, then act on behalf of the trim thread.
// See the code in ThreadList::RunCheckpoint.
- if (thread->GetState() == kRunnable) {
- barrier_->Pass(Thread::Current());
- }
+ barrier_->Pass(Thread::Current());
}
private:
diff --git a/runtime/image.cc b/runtime/image.cc
index 192371fe75..1bc19ff656 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -150,10 +150,10 @@ std::ostream& operator<<(std::ostream& os, const ImageSection& section) {
void ImageSection::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const {
for (size_t pos = 0; pos < Size(); ) {
auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + Offset() + pos);
- for (size_t i = 0; i < array->Length(); ++i) {
+ for (size_t i = 0; i < array->size(); ++i) {
visitor->Visit(&array->At(i, sizeof(ArtField)));
}
- pos += array->ComputeSize(array->Length());
+ pos += array->ComputeSize(array->size());
}
}
@@ -164,10 +164,10 @@ void ImageSection::VisitPackedArtMethods(ArtMethodVisitor* visitor,
const size_t method_size = ArtMethod::Size(pointer_size);
for (size_t pos = 0; pos < Size(); ) {
auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + Offset() + pos);
- for (size_t i = 0; i < array->Length(); ++i) {
+ for (size_t i = 0; i < array->size(); ++i) {
visitor->Visit(&array->At(i, method_size, method_alignment));
}
- pos += array->ComputeSize(array->Length(), method_size, method_alignment);
+ pos += array->ComputeSize(array->size(), method_size, method_alignment);
}
}
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index ed64d7efbe..4db37e600a 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -97,16 +97,6 @@ void Instrumentation::InstallStubsForClass(mirror::Class* klass) {
static void UpdateEntrypoints(ArtMethod* method, const void* quick_code)
SHARED_REQUIRES(Locks::mutator_lock_) {
- Runtime* const runtime = Runtime::Current();
- jit::Jit* jit = runtime->GetJit();
- if (jit != nullptr) {
- const void* old_code_ptr = method->GetEntryPointFromQuickCompiledCode();
- jit::JitCodeCache* code_cache = jit->GetCodeCache();
- if (code_cache->ContainsCodePtr(old_code_ptr)) {
- // Save the old compiled code since we need it to implement ClassLinker::GetQuickOatCodeFor.
- code_cache->SaveCompiledCode(method, old_code_ptr);
- }
- }
method->SetEntryPointFromQuickCompiledCode(quick_code);
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 0607493420..5afd28e7b5 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -49,7 +49,7 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt
void Jit::DumpInfo(std::ostream& os) {
os << "Code cache size=" << PrettySize(code_cache_->CodeCacheSize())
<< " data cache size=" << PrettySize(code_cache_->DataCacheSize())
- << " num methods=" << code_cache_->NumMethods()
+ << " number of compiled code=" << code_cache_->NumberOfCompiledCode()
<< "\n";
cumulative_timings_.Dump(os);
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index e73ba82278..1f89f9b1b7 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -43,7 +43,7 @@ class JitOptions;
class Jit {
public:
static constexpr bool kStressMode = kIsDebugBuild;
- static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 1000;
+ static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 500;
static constexpr size_t kDefaultWarmupThreshold = kDefaultCompileThreshold / 2;
virtual ~Jit();
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4187358bc0..ce972ef976 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -19,8 +19,14 @@
#include <sstream>
#include "art_method-inl.h"
+#include "entrypoints/runtime_asm_entrypoints.h"
+#include "gc/accounting/bitmap-inl.h"
+#include "jit/profiling_info.h"
+#include "linear_alloc.h"
#include "mem_map.h"
#include "oat_file-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread_list.h"
namespace art {
namespace jit {
@@ -52,9 +58,9 @@ JitCodeCache* JitCodeCache::Create(size_t capacity, std::string* error_msg) {
return nullptr;
}
- // Data cache is 1 / 4 of the map.
+ // Data cache is 1 / 2 of the map.
// TODO: Make this variable?
- size_t data_size = RoundUp(data_map->Size() / 4, kPageSize);
+ size_t data_size = RoundUp(data_map->Size() / 2, kPageSize);
size_t code_size = data_map->Size() - data_size;
uint8_t* divider = data_map->Begin() + data_size;
@@ -74,14 +80,10 @@ JitCodeCache* JitCodeCache::Create(size_t capacity, std::string* error_msg) {
JitCodeCache::JitCodeCache(MemMap* code_map, MemMap* data_map)
: lock_("Jit code cache", kJitCodeCacheLock),
+ lock_cond_("Jit code cache variable", lock_),
+ collection_in_progress_(false),
code_map_(code_map),
- data_map_(data_map),
- num_methods_(0) {
-
- VLOG(jit) << "Created jit code cache: data size="
- << PrettySize(data_map_->Size())
- << ", code size="
- << PrettySize(code_map_->Size());
+ data_map_(data_map) {
code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_map_->Size(), false /*locked*/);
data_mspace_ = create_mspace_with_base(data_map_->Begin(), data_map_->Size(), false /*locked*/);
@@ -96,13 +98,22 @@ JitCodeCache::JitCodeCache(MemMap* code_map, MemMap* data_map)
CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode);
CHECKED_MPROTECT(data_map_->Begin(), data_map_->Size(), kProtData);
-}
-bool JitCodeCache::ContainsMethod(ArtMethod* method) const {
- return ContainsCodePtr(method->GetEntryPointFromQuickCompiledCode());
+ live_bitmap_.reset(CodeCacheBitmap::Create("code-cache-bitmap",
+ reinterpret_cast<uintptr_t>(code_map_->Begin()),
+ reinterpret_cast<uintptr_t>(code_map_->End())));
+
+ if (live_bitmap_.get() == nullptr) {
+ PLOG(FATAL) << "creating bitmaps for the JIT code cache failed";
+ }
+
+ VLOG(jit) << "Created jit code cache: data size="
+ << PrettySize(data_map_->Size())
+ << ", code size="
+ << PrettySize(code_map_->Size());
}
-bool JitCodeCache::ContainsCodePtr(const void* ptr) const {
+bool JitCodeCache::ContainsPc(const void* ptr) const {
return code_map_->Begin() <= ptr && ptr < code_map_->End();
}
@@ -121,6 +132,7 @@ class ScopedCodeCacheWrite {
};
uint8_t* JitCodeCache::CommitCode(Thread* self,
+ ArtMethod* method,
const uint8_t* mapping_table,
const uint8_t* vmap_table,
const uint8_t* gc_map,
@@ -129,6 +141,106 @@ uint8_t* JitCodeCache::CommitCode(Thread* self,
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size) {
+ uint8_t* result = CommitCodeInternal(self,
+ method,
+ mapping_table,
+ vmap_table,
+ gc_map,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ code,
+ code_size);
+ if (result == nullptr) {
+ // Retry.
+ GarbageCollectCache(self);
+ result = CommitCodeInternal(self,
+ method,
+ mapping_table,
+ vmap_table,
+ gc_map,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ code,
+ code_size);
+ }
+ return result;
+}
+
+bool JitCodeCache::WaitForPotentialCollectionToComplete(Thread* self) {
+ bool in_collection = false;
+ while (collection_in_progress_) {
+ in_collection = true;
+ lock_cond_.Wait(self);
+ }
+ return in_collection;
+}
+
+static uintptr_t FromCodeToAllocation(const void* code) {
+ size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ return reinterpret_cast<uintptr_t>(code) - RoundUp(sizeof(OatQuickMethodHeader), alignment);
+}
+
+void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) {
+ uintptr_t allocation = FromCodeToAllocation(code_ptr);
+ const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ const uint8_t* data = method_header->GetNativeGcMap();
+ if (data != nullptr) {
+ mspace_free(data_mspace_, const_cast<uint8_t*>(data));
+ }
+ data = method_header->GetMappingTable();
+ if (data != nullptr) {
+ mspace_free(data_mspace_, const_cast<uint8_t*>(data));
+ }
+ // Use the offset directly to prevent sanity check that the method is
+ // compiled with optimizing.
+ // TODO(ngeoffray): Clean up.
+ if (method_header->vmap_table_offset_ != 0) {
+ data = method_header->code_ - method_header->vmap_table_offset_;
+ mspace_free(data_mspace_, const_cast<uint8_t*>(data));
+ }
+ mspace_free(code_mspace_, reinterpret_cast<uint8_t*>(allocation));
+}
+
+void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) {
+ MutexLock mu(self, lock_);
+ // We do not check if a code cache GC is in progress, as this method comes
+ // with the classlinker_classes_lock_ held, and suspending ourselves could
+ // lead to a deadlock.
+ {
+ ScopedCodeCacheWrite scc(code_map_.get());
+ for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
+ if (alloc.ContainsUnsafe(it->second)) {
+ FreeCode(it->first, it->second);
+ it = method_code_map_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ for (auto it = profiling_infos_.begin(); it != profiling_infos_.end();) {
+ ProfilingInfo* info = *it;
+ if (alloc.ContainsUnsafe(info->GetMethod())) {
+ info->GetMethod()->SetProfilingInfo(nullptr);
+ mspace_free(data_mspace_, reinterpret_cast<uint8_t*>(info));
+ it = profiling_infos_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
+ ArtMethod* method,
+ const uint8_t* mapping_table,
+ const uint8_t* vmap_table,
+ const uint8_t* gc_map,
+ size_t frame_size_in_bytes,
+ size_t core_spill_mask,
+ size_t fp_spill_mask,
+ const uint8_t* code,
+ size_t code_size) {
size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
// Ensure the header ends up at expected instruction alignment.
size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
@@ -137,7 +249,9 @@ uint8_t* JitCodeCache::CommitCode(Thread* self,
OatQuickMethodHeader* method_header = nullptr;
uint8_t* code_ptr = nullptr;
+ ScopedThreadSuspension sts(self, kSuspended);
MutexLock mu(self, lock_);
+ WaitForPotentialCollectionToComplete(self);
{
ScopedCodeCacheWrite scc(code_map_.get());
uint8_t* result = reinterpret_cast<uint8_t*>(
@@ -149,7 +263,7 @@ uint8_t* JitCodeCache::CommitCode(Thread* self,
DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(code_ptr), alignment);
std::copy(code, code + code_size, code_ptr);
- method_header = reinterpret_cast<OatQuickMethodHeader*>(code_ptr) - 1;
+ method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
new (method_header) OatQuickMethodHeader(
(mapping_table == nullptr) ? 0 : code_ptr - mapping_table,
(vmap_table == nullptr) ? 0 : code_ptr - vmap_table,
@@ -162,8 +276,12 @@ uint8_t* JitCodeCache::CommitCode(Thread* self,
__builtin___clear_cache(reinterpret_cast<char*>(code_ptr),
reinterpret_cast<char*>(code_ptr + code_size));
-
- ++num_methods_; // TODO: This is hacky but works since each method has exactly one code region.
+ method_code_map_.Put(code_ptr, method);
+ // We have checked there was no collection in progress earlier. If we
+ // were, setting the entry point of a method would be unsafe, as the collection
+ // could delete it.
+ DCHECK(!collection_in_progress_);
+ method->SetEntryPointFromQuickCompiledCode(method_header->GetEntryPoint());
return reinterpret_cast<uint8_t*>(method_header);
}
@@ -181,10 +299,32 @@ size_t JitCodeCache::DataCacheSize() {
return bytes_allocated;
}
+size_t JitCodeCache::NumberOfCompiledCode() {
+ MutexLock mu(Thread::Current(), lock_);
+ return method_code_map_.size();
+}
+
uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
size = RoundUp(size, sizeof(void*));
- MutexLock mu(self, lock_);
- return reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size));
+ uint8_t* result = nullptr;
+
+ {
+ ScopedThreadSuspension sts(self, kSuspended);
+ MutexLock mu(self, lock_);
+ WaitForPotentialCollectionToComplete(self);
+ result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size));
+ }
+
+ if (result == nullptr) {
+ // Retry.
+ GarbageCollectCache(self);
+ ScopedThreadSuspension sts(self, kSuspended);
+ MutexLock mu(self, lock_);
+ WaitForPotentialCollectionToComplete(self);
+ result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size));
+ }
+
+ return result;
}
uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) {
@@ -196,29 +336,201 @@ uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const ui
return result;
}
-const void* JitCodeCache::GetCodeFor(ArtMethod* method) {
- const void* code = method->GetEntryPointFromQuickCompiledCode();
- if (ContainsCodePtr(code)) {
- return code;
+class MarkCodeVisitor FINAL : public StackVisitor {
+ public:
+ MarkCodeVisitor(Thread* thread_in, JitCodeCache* code_cache_in)
+ : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kSkipInlinedFrames),
+ code_cache_(code_cache_in),
+ bitmap_(code_cache_->GetLiveBitmap()) {}
+
+ bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ if (method_header == nullptr) {
+ return true;
+ }
+ const void* code = method_header->GetCode();
+ if (code_cache_->ContainsPc(code)) {
+ // Use the atomic set version, as multiple threads are executing this code.
+ bitmap_->AtomicTestAndSet(FromCodeToAllocation(code));
+ }
+ return true;
}
- MutexLock mu(Thread::Current(), lock_);
- auto it = method_code_map_.find(method);
- if (it != method_code_map_.end()) {
- return it->second;
+
+ private:
+ JitCodeCache* const code_cache_;
+ CodeCacheBitmap* const bitmap_;
+};
+
+class MarkCodeClosure FINAL : public Closure {
+ public:
+ MarkCodeClosure(JitCodeCache* code_cache, Barrier* barrier)
+ : code_cache_(code_cache), barrier_(barrier) {}
+
+ void Run(Thread* thread) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(thread == Thread::Current() || thread->IsSuspended());
+ MarkCodeVisitor visitor(thread, code_cache_);
+ visitor.WalkStack();
+ barrier_->Pass(Thread::Current());
+ }
+
+ private:
+ JitCodeCache* const code_cache_;
+ Barrier* const barrier_;
+};
+
+void JitCodeCache::GarbageCollectCache(Thread* self) {
+ if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
+ LOG(INFO) << "Clearing code cache, code="
+ << PrettySize(CodeCacheSize())
+ << ", data=" << PrettySize(DataCacheSize());
+ }
+
+ size_t map_size = 0;
+ ScopedThreadSuspension sts(self, kSuspended);
+
+ // Walk over all compiled methods and set the entry points of these
+ // methods to interpreter.
+ {
+ MutexLock mu(self, lock_);
+ if (WaitForPotentialCollectionToComplete(self)) {
+ return;
+ }
+ collection_in_progress_ = true;
+ map_size = method_code_map_.size();
+ for (auto& it : method_code_map_) {
+ it.second->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+ }
+ for (ProfilingInfo* info : profiling_infos_) {
+ info->GetMethod()->SetProfilingInfo(nullptr);
+ }
+ }
+
+ // Run a checkpoint on all threads to mark the JIT compiled code they are running.
+ {
+ Barrier barrier(0);
+ size_t threads_running_checkpoint = 0;
+ {
+ // Walking the stack requires the mutator lock.
+ // We only take the lock when running the checkpoint and not waiting so that
+ // when we go back to suspended, we can execute checkpoints that were requested
+ // concurrently, and then move to waiting for our own checkpoint to finish.
+ ScopedObjectAccess soa(self);
+ MarkCodeClosure closure(this, &barrier);
+ threads_running_checkpoint =
+ Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
+ }
+ if (threads_running_checkpoint != 0) {
+ barrier.Increment(self, threads_running_checkpoint);
+ }
+ }
+
+ {
+ MutexLock mu(self, lock_);
+ DCHECK_EQ(map_size, method_code_map_.size());
+ // Free unused compiled code, and restore the entry point of used compiled code.
+ {
+ ScopedCodeCacheWrite scc(code_map_.get());
+ for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
+ const void* code_ptr = it->first;
+ ArtMethod* method = it->second;
+ uintptr_t allocation = FromCodeToAllocation(code_ptr);
+ const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ if (GetLiveBitmap()->Test(allocation)) {
+ method->SetEntryPointFromQuickCompiledCode(method_header->GetEntryPoint());
+ ++it;
+ } else {
+ method->ClearCounter();
+ DCHECK_NE(method->GetEntryPointFromQuickCompiledCode(), method_header->GetEntryPoint());
+ FreeCode(code_ptr, method);
+ it = method_code_map_.erase(it);
+ }
+ }
+ }
+ GetLiveBitmap()->Bitmap::Clear();
+
+ // Free all profiling info.
+ for (ProfilingInfo* info : profiling_infos_) {
+ DCHECK(info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr);
+ mspace_free(data_mspace_, reinterpret_cast<uint8_t*>(info));
+ }
+ profiling_infos_.clear();
+
+ collection_in_progress_ = false;
+ lock_cond_.Broadcast(self);
+ }
+
+ if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
+ LOG(INFO) << "After clearing code cache, code="
+ << PrettySize(CodeCacheSize())
+ << ", data=" << PrettySize(DataCacheSize());
}
- return nullptr;
}
-void JitCodeCache::SaveCompiledCode(ArtMethod* method, const void* old_code_ptr) {
- DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), old_code_ptr);
- DCHECK(ContainsCodePtr(old_code_ptr)) << PrettyMethod(method) << " old_code_ptr="
- << old_code_ptr;
+
+OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* method) {
+ static_assert(kRuntimeISA != kThumb2, "kThumb2 cannot be a runtime ISA");
+ if (kRuntimeISA == kArm) {
+ // On Thumb-2, the pc is offset by one.
+ --pc;
+ }
+ if (!ContainsPc(reinterpret_cast<const void*>(pc))) {
+ return nullptr;
+ }
+
MutexLock mu(Thread::Current(), lock_);
- auto it = method_code_map_.find(method);
- if (it != method_code_map_.end()) {
- return;
+ if (method_code_map_.empty()) {
+ return nullptr;
+ }
+ auto it = method_code_map_.lower_bound(reinterpret_cast<const void*>(pc));
+ --it;
+
+ const void* code_ptr = it->first;
+ OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ if (!method_header->Contains(pc)) {
+ return nullptr;
+ }
+ DCHECK_EQ(it->second, method)
+ << PrettyMethod(method) << " " << PrettyMethod(it->second) << " " << std::hex << pc;
+ return method_header;
+}
+
+ProfilingInfo* JitCodeCache::AddProfilingInfo(Thread* self,
+ ArtMethod* method,
+ const std::vector<uint32_t>& entries,
+ bool retry_allocation) {
+ ProfilingInfo* info = AddProfilingInfoInternal(self, method, entries);
+
+ if (info == nullptr && retry_allocation) {
+ GarbageCollectCache(self);
+ info = AddProfilingInfoInternal(self, method, entries);
+ }
+ return info;
+}
+
+ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self,
+ ArtMethod* method,
+ const std::vector<uint32_t>& entries) {
+ size_t profile_info_size = RoundUp(
+ sizeof(ProfilingInfo) + sizeof(ProfilingInfo::InlineCache) * entries.size(),
+ sizeof(void*));
+ ScopedThreadSuspension sts(self, kSuspended);
+ MutexLock mu(self, lock_);
+ WaitForPotentialCollectionToComplete(self);
+
+ // Check whether some other thread has concurrently created it.
+ ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
+ if (info != nullptr) {
+ return info;
+ }
+
+ uint8_t* data = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, profile_info_size));
+ if (data == nullptr) {
+ return nullptr;
}
- method_code_map_.Put(method, old_code_ptr);
+ info = new (data) ProfilingInfo(method, entries);
+ method->SetProfilingInfo(info);
+ profiling_infos_.push_back(info);
+ return info;
}
} // namespace jit
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index fa90c1806f..e10f9629ae 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -22,6 +22,7 @@
#include "atomic.h"
#include "base/macros.h"
#include "base/mutex.h"
+#include "gc/accounting/bitmap.h"
#include "gc/allocator/dlmalloc.h"
#include "gc_root.h"
#include "jni.h"
@@ -33,32 +34,41 @@
namespace art {
class ArtMethod;
-class CompiledMethod;
-class CompilerCallbacks;
+class LinearAlloc;
+class ProfilingInfo;
namespace jit {
class JitInstrumentationCache;
+// Alignment that will suit all architectures.
+static constexpr int kJitCodeAlignment = 16;
+using CodeCacheBitmap = gc::accounting::MemoryRangeBitmap<kJitCodeAlignment>;
+
class JitCodeCache {
public:
static constexpr size_t kMaxCapacity = 1 * GB;
- static constexpr size_t kDefaultCapacity = 2 * MB;
+ // Put the default to a very low amount for debug builds to stress the code cache
+ // collection.
+ static constexpr size_t kDefaultCapacity = kIsDebugBuild ? 20 * KB : 2 * MB;
// Create the code cache with a code + data capacity equal to "capacity", error message is passed
// in the out arg error_msg.
static JitCodeCache* Create(size_t capacity, std::string* error_msg);
- size_t NumMethods() const {
- return num_methods_;
- }
-
+ // Number of bytes allocated in the code cache.
size_t CodeCacheSize() REQUIRES(!lock_);
+ // Number of bytes allocated in the data cache.
size_t DataCacheSize() REQUIRES(!lock_);
+ // Number of compiled code in the code cache. Note that this is not the number
+ // of methods that got JIT compiled, as we might have collected some.
+ size_t NumberOfCompiledCode() REQUIRES(!lock_);
+
// Allocate and write code and its metadata to the code cache.
uint8_t* CommitCode(Thread* self,
+ ArtMethod* method,
const uint8_t* mapping_table,
const uint8_t* vmap_table,
const uint8_t* gc_map,
@@ -67,51 +77,107 @@ class JitCodeCache {
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size)
+ SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
- // Return true if the code cache contains the code pointer which si the entrypoint of the method.
- bool ContainsMethod(ArtMethod* method) const
- SHARED_REQUIRES(Locks::mutator_lock_);
-
- // Return true if the code cache contains a code ptr.
- bool ContainsCodePtr(const void* ptr) const;
+ // Return true if the code cache contains this pc.
+ bool ContainsPc(const void* pc) const;
// Reserve a region of data of size at least "size". Returns null if there is no more room.
- uint8_t* ReserveData(Thread* self, size_t size) REQUIRES(!lock_);
+ uint8_t* ReserveData(Thread* self, size_t size)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!lock_);
// Add a data array of size (end - begin) with the associated contents, returns null if there
// is no more room.
uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end)
+ SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
- // Get code for a method, returns null if it is not in the jit cache.
- const void* GetCodeFor(ArtMethod* method)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
+ CodeCacheBitmap* GetLiveBitmap() const {
+ return live_bitmap_.get();
+ }
- // Save the compiled code for a method so that GetCodeFor(method) will return old_code_ptr if the
- // entrypoint isn't within the cache.
- void SaveCompiledCode(ArtMethod* method, const void* old_code_ptr)
- SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!lock_);
+ // Perform a collection on the code cache.
+ void GarbageCollectCache(Thread* self)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Given the 'pc', try to find the JIT compiled code associated with it.
+ // Return null if 'pc' is not in the code cache. 'method' is passed for
+ // sanity check.
+ OatQuickMethodHeader* LookupMethodHeader(uintptr_t pc, ArtMethod* method)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Remove all methods in our cache that were allocated by 'alloc'.
+ void RemoveMethodsIn(Thread* self, const LinearAlloc& alloc)
+ REQUIRES(!lock_)
+ REQUIRES(Locks::classlinker_classes_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // Create a 'ProfileInfo' for 'method'. If 'retry_allocation' is true,
+ // will collect and retry if the first allocation is unsuccessful.
+ ProfilingInfo* AddProfilingInfo(Thread* self,
+ ArtMethod* method,
+ const std::vector<uint32_t>& entries,
+ bool retry_allocation)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
private:
- // Takes ownership of code_mem_map.
+ // Take ownership of code_mem_map.
JitCodeCache(MemMap* code_map, MemMap* data_map);
- // Lock which guards.
+ // Internal version of 'CommitCode' that will not retry if the
+ // allocation fails. Return null if the allocation fails.
+ uint8_t* CommitCodeInternal(Thread* self,
+ ArtMethod* method,
+ const uint8_t* mapping_table,
+ const uint8_t* vmap_table,
+ const uint8_t* gc_map,
+ size_t frame_size_in_bytes,
+ size_t core_spill_mask,
+ size_t fp_spill_mask,
+ const uint8_t* code,
+ size_t code_size)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ ProfilingInfo* AddProfilingInfoInternal(Thread* self,
+ ArtMethod* method,
+ const std::vector<uint32_t>& entries)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ // If a collection is in progress, wait for it to finish. Return
+ // whether the thread actually waited.
+ bool WaitForPotentialCollectionToComplete(Thread* self)
+ REQUIRES(lock_) REQUIRES(!Locks::mutator_lock_);
+
+ // Free in the mspace allocations taken by 'method'.
+ void FreeCode(const void* code_ptr, ArtMethod* method) REQUIRES(lock_);
+
+ // Lock for guarding allocations, collections, and the method_code_map_.
Mutex lock_;
+ // Condition to wait on during collection.
+ ConditionVariable lock_cond_ GUARDED_BY(lock_);
+ // Whether there is a code cache collection in progress.
+ bool collection_in_progress_ GUARDED_BY(lock_);
// Mem map which holds code.
std::unique_ptr<MemMap> code_map_;
// Mem map which holds data (stack maps and profiling info).
std::unique_ptr<MemMap> data_map_;
// The opaque mspace for allocating code.
- void* code_mspace_;
+ void* code_mspace_ GUARDED_BY(lock_);
// The opaque mspace for allocating data.
- void* data_mspace_;
- // Number of compiled methods.
- size_t num_methods_;
- // This map holds code for methods if they were deoptimized by the instrumentation stubs. This is
- // required since we have to implement ClassLinker::GetQuickOatCodeFor for walking stacks.
- SafeMap<ArtMethod*, const void*> method_code_map_ GUARDED_BY(lock_);
+ void* data_mspace_ GUARDED_BY(lock_);
+ // Bitmap for collecting code and data.
+ std::unique_ptr<CodeCacheBitmap> live_bitmap_;
+ // This map holds compiled code associated to the ArtMethod.
+ SafeMap<const void*, ArtMethod*> method_code_map_ GUARDED_BY(lock_);
+ // ProfilingInfo objects we have allocated.
+ std::vector<ProfilingInfo*> profiling_infos_ GUARDED_BY(lock_);
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
};
diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc
index 9b9c5d2760..7931306ff6 100644
--- a/runtime/jit/jit_instrumentation.cc
+++ b/runtime/jit/jit_instrumentation.cc
@@ -26,7 +26,12 @@ namespace jit {
class JitCompileTask FINAL : public Task {
public:
- explicit JitCompileTask(ArtMethod* method) : method_(method) {
+ enum TaskKind {
+ kAllocateProfile,
+ kCompile
+ };
+
+ JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) {
ScopedObjectAccess soa(Thread::Current());
// Add a global ref to the class to prevent class unloading until compilation is done.
klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
@@ -40,9 +45,16 @@ class JitCompileTask FINAL : public Task {
void Run(Thread* self) OVERRIDE {
ScopedObjectAccess soa(self);
- VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
- if (!Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
- VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
+ if (kind_ == kCompile) {
+ VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
+ if (!Runtime::Current()->GetJit()->CompileMethod(method_, self)) {
+ VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
+ }
+ } else {
+ DCHECK(kind_ == kAllocateProfile);
+ if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
+ VLOG(jit) << "Start profiling " << PrettyMethod(method_);
+ }
}
}
@@ -52,6 +64,7 @@ class JitCompileTask FINAL : public Task {
private:
ArtMethod* const method_;
+ const TaskKind kind_;
jobject klass_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
@@ -73,11 +86,9 @@ void JitInstrumentationCache::DeleteThreadPool() {
}
void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t) {
- ScopedObjectAccessUnchecked soa(self);
// Since we don't have on-stack replacement, some methods can remain in the interpreter longer
// than we want resulting in samples even after the method is compiled.
- if (method->IsClassInitializer() || method->IsNative() ||
- Runtime::Current()->GetJit()->GetCodeCache()->ContainsMethod(method)) {
+ if (method->IsClassInitializer() || method->IsNative()) {
return;
}
if (thread_pool_.get() == nullptr) {
@@ -86,14 +97,18 @@ void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, size_t
}
uint16_t sample_count = method->IncrementCounter();
if (sample_count == warm_method_threshold_) {
- ProfilingInfo* info = method->CreateProfilingInfo();
- if (info != nullptr) {
+ if (ProfilingInfo::Create(self, method, /* retry_allocation */ false)) {
VLOG(jit) << "Start profiling " << PrettyMethod(method);
+ } else {
+ // We failed allocating. Instead of doing the collection on the Java thread, we push
+ // an allocation to a compiler thread, that will do the collection.
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile));
+ thread_pool_->StartWorkers(self);
}
}
+
if (sample_count == hot_method_threshold_) {
- thread_pool_->AddTask(self, new JitCompileTask(
- method->GetInterfaceMethodIfProxy(sizeof(void*))));
+ thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
thread_pool_->StartWorkers(self);
}
}
@@ -108,14 +123,18 @@ void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread,
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee ATTRIBUTE_UNUSED) {
+ instrumentation_cache_->AddSamples(thread, caller, 1);
+ // We make sure we cannot be suspended, as the profiling info can be concurrently deleted.
+ thread->StartAssertNoThreadSuspension("Instrumenting invoke");
DCHECK(this_object != nullptr);
ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*));
if (info != nullptr) {
// Since the instrumentation is marked from the declaring class we need to mark the card so
// that mod-union tables and card rescanning know about the update.
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass());
- info->AddInvokeInfo(thread, dex_pc, this_object->GetClass());
+ info->AddInvokeInfo(dex_pc, this_object->GetClass());
}
+ thread->EndAssertNoThreadSuspension(nullptr);
}
void JitInstrumentationCache::WaitForCompilationToFinish(Thread* self) {
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 0c039f2bbd..2e52b1b4dc 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -25,18 +25,13 @@
namespace art {
-ProfilingInfo* ProfilingInfo::Create(ArtMethod* method) {
+bool ProfilingInfo::Create(Thread* self, ArtMethod* method, bool retry_allocation) {
// Walk over the dex instructions of the method and keep track of
// instructions we are interested in profiling.
- const uint16_t* code_ptr = nullptr;
- const uint16_t* code_end = nullptr;
- {
- ScopedObjectAccess soa(Thread::Current());
- DCHECK(!method->IsNative());
- const DexFile::CodeItem& code_item = *method->GetCodeItem();
- code_ptr = code_item.insns_;
- code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
- }
+ DCHECK(!method->IsNative());
+ const DexFile::CodeItem& code_item = *method->GetCodeItem();
+ const uint16_t* code_ptr = code_item.insns_;
+ const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
uint32_t dex_pc = 0;
std::vector<uint32_t> entries;
@@ -62,23 +57,15 @@ ProfilingInfo* ProfilingInfo::Create(ArtMethod* method) {
// If there is no instruction we are interested in, no need to create a `ProfilingInfo`
// object, it will never be filled.
if (entries.empty()) {
- return nullptr;
+ return true;
}
// Allocate the `ProfilingInfo` object int the JIT's data space.
jit::JitCodeCache* code_cache = Runtime::Current()->GetJit()->GetCodeCache();
- size_t profile_info_size = sizeof(ProfilingInfo) + sizeof(InlineCache) * entries.size();
- uint8_t* data = code_cache->ReserveData(Thread::Current(), profile_info_size);
-
- if (data == nullptr) {
- VLOG(jit) << "Cannot allocate profiling info anymore";
- return nullptr;
- }
-
- return new (data) ProfilingInfo(entries);
+ return code_cache->AddProfilingInfo(self, method, entries, retry_allocation) != nullptr;
}
-void ProfilingInfo::AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls) {
+void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) {
InlineCache* cache = nullptr;
// TODO: binary search if array is too long.
for (size_t i = 0; i < number_of_inline_caches_; ++i) {
@@ -89,9 +76,8 @@ void ProfilingInfo::AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class*
}
DCHECK(cache != nullptr);
- ScopedObjectAccess soa(self);
for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
- mirror::Class* existing = cache->classes_[i].Read<kWithoutReadBarrier>();
+ mirror::Class* existing = cache->classes_[i].Read();
if (existing == cls) {
// Receiver type is already in the cache, nothing else to do.
return;
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 73ca41a9a1..b13a315d64 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -26,6 +26,10 @@ namespace art {
class ArtMethod;
+namespace jit {
+class JitCodeCache;
+}
+
namespace mirror {
class Class;
}
@@ -36,10 +40,17 @@ class Class;
*/
class ProfilingInfo {
public:
- static ProfilingInfo* Create(ArtMethod* method);
+ // Create a ProfilingInfo for 'method'. Return whether it succeeded, or if it is
+ // not needed in case the method does not have virtual/interface invocations.
+ static bool Create(Thread* self, ArtMethod* method, bool retry_allocation)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Add information from an executed INVOKE instruction to the profile.
- void AddInvokeInfo(Thread* self, uint32_t dex_pc, mirror::Class* cls);
+ void AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls)
+ // Method should not be interruptible, as it manipulates the ProfilingInfo
+ // which can be concurrently collected.
+ REQUIRES(Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
template<typename RootVisitorType>
@@ -52,6 +63,10 @@ class ProfilingInfo {
}
}
+ ArtMethod* GetMethod() const {
+ return method_;
+ }
+
private:
// Structure to store the classes seen at runtime for a specific instruction.
// Once the classes_ array is full, we consider the INVOKE to be megamorphic.
@@ -84,8 +99,9 @@ class ProfilingInfo {
GcRoot<mirror::Class> classes_[kIndividualCacheSize];
};
- explicit ProfilingInfo(const std::vector<uint32_t>& entries)
- : number_of_inline_caches_(entries.size()) {
+ ProfilingInfo(ArtMethod* method, const std::vector<uint32_t>& entries)
+ : number_of_inline_caches_(entries.size()),
+ method_(method) {
memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
for (size_t i = 0; i < number_of_inline_caches_; ++i) {
cache_[i].dex_pc = entries[i];
@@ -95,9 +111,14 @@ class ProfilingInfo {
// Number of instructions we are profiling in the ArtMethod.
const uint32_t number_of_inline_caches_;
+ // Method this profiling info is for.
+ ArtMethod* const method_;
+
// Dynamically allocated array of size `number_of_inline_caches_`.
InlineCache cache_[0];
+ friend class jit::JitCodeCache;
+
DISALLOW_COPY_AND_ASSIGN(ProfilingInfo);
};
diff --git a/runtime/length_prefixed_array.h b/runtime/length_prefixed_array.h
index 0ff6d7a9aa..e01b6ccd26 100644
--- a/runtime/length_prefixed_array.h
+++ b/runtime/length_prefixed_array.h
@@ -30,19 +30,34 @@ template<typename T>
class LengthPrefixedArray {
public:
explicit LengthPrefixedArray(size_t length)
- : length_(dchecked_integral_cast<uint32_t>(length)) {}
+ : size_(dchecked_integral_cast<uint32_t>(length)) {}
T& At(size_t index, size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
- DCHECK_LT(index, length_);
+ DCHECK_LT(index, size_);
return AtUnchecked(index, element_size, alignment);
}
- StrideIterator<T> Begin(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
+ const T& At(size_t index, size_t element_size = sizeof(T), size_t alignment = alignof(T)) const {
+ DCHECK_LT(index, size_);
+ return AtUnchecked(index, element_size, alignment);
+ }
+
+ StrideIterator<T> begin(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
return StrideIterator<T>(&AtUnchecked(0, element_size, alignment), element_size);
}
- StrideIterator<T> End(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
- return StrideIterator<T>(&AtUnchecked(length_, element_size, alignment), element_size);
+ StrideIterator<const T> begin(size_t element_size = sizeof(T),
+ size_t alignment = alignof(T)) const {
+ return StrideIterator<const T>(&AtUnchecked(0, element_size, alignment), element_size);
+ }
+
+ StrideIterator<T> end(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
+ return StrideIterator<T>(&AtUnchecked(size_, element_size, alignment), element_size);
+ }
+
+ StrideIterator<const T> end(size_t element_size = sizeof(T),
+ size_t alignment = alignof(T)) const {
+ return StrideIterator<const T>(&AtUnchecked(size_, element_size, alignment), element_size);
}
static size_t OffsetOfElement(size_t index,
@@ -60,13 +75,13 @@ class LengthPrefixedArray {
return result;
}
- uint64_t Length() const {
- return length_;
+ size_t size() const {
+ return size_;
}
// Update the length but does not reallocate storage.
- void SetLength(size_t length) {
- length_ = dchecked_integral_cast<uint32_t>(length);
+ void SetSize(size_t length) {
+ size_ = dchecked_integral_cast<uint32_t>(length);
}
private:
@@ -75,7 +90,12 @@ class LengthPrefixedArray {
reinterpret_cast<uintptr_t>(this) + OffsetOfElement(index, element_size, alignment));
}
- uint32_t length_;
+ const T& AtUnchecked(size_t index, size_t element_size, size_t alignment) const {
+ return *reinterpret_cast<T*>(
+ reinterpret_cast<uintptr_t>(this) + OffsetOfElement(index, element_size, alignment));
+ }
+
+ uint32_t size_;
uint8_t data[0];
};
@@ -84,7 +104,7 @@ template<typename T>
IterationRange<StrideIterator<T>> MakeIterationRangeFromLengthPrefixedArray(
LengthPrefixedArray<T>* arr, size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
return arr != nullptr ?
- MakeIterationRange(arr->Begin(element_size, alignment), arr->End(element_size, alignment)) :
+ MakeIterationRange(arr->begin(element_size, alignment), arr->end(element_size, alignment)) :
MakeEmptyIterationRange(StrideIterator<T>(nullptr, 0));
}
diff --git a/runtime/linear_alloc.cc b/runtime/linear_alloc.cc
index 43e81d9d94..f91b0ed9ea 100644
--- a/runtime/linear_alloc.cc
+++ b/runtime/linear_alloc.cc
@@ -48,4 +48,8 @@ bool LinearAlloc::Contains(void* ptr) const {
return allocator_.Contains(ptr);
}
+bool LinearAlloc::ContainsUnsafe(void* ptr) const {
+ return allocator_.Contains(ptr);
+}
+
} // namespace art
diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h
index 1b21527317..df7f17dd7a 100644
--- a/runtime/linear_alloc.h
+++ b/runtime/linear_alloc.h
@@ -47,6 +47,10 @@ class LinearAlloc {
// Return true if the linear alloc contrains an address.
bool Contains(void* ptr) const REQUIRES(!lock_);
+ // Unsafe version of 'Contains' only to be used when the allocator is going
+ // to be deleted.
+ bool ContainsUnsafe(void* ptr) const NO_THREAD_SAFETY_ANALYSIS;
+
private:
mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ArenaAllocator allocator_ GUARDED_BY(lock_);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index a528c3b890..19ee7f4dd4 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -928,22 +928,22 @@ inline bool Class::IsAssignableFrom(Class* src) {
inline uint32_t Class::NumDirectMethods() {
LengthPrefixedArray<ArtMethod>* arr = GetDirectMethodsPtrUnchecked();
- return arr != nullptr ? arr->Length() : 0u;
+ return arr != nullptr ? arr->size() : 0u;
}
inline uint32_t Class::NumVirtualMethods() {
LengthPrefixedArray<ArtMethod>* arr = GetVirtualMethodsPtrUnchecked();
- return arr != nullptr ? arr->Length() : 0u;
+ return arr != nullptr ? arr->size() : 0u;
}
inline uint32_t Class::NumInstanceFields() {
LengthPrefixedArray<ArtField>* arr = GetIFieldsPtrUnchecked();
- return arr != nullptr ? arr->Length() : 0u;
+ return arr != nullptr ? arr->size() : 0u;
}
inline uint32_t Class::NumStaticFields() {
LengthPrefixedArray<ArtField>* arr = GetSFieldsPtrUnchecked();
- return arr != nullptr ? arr->Length() : 0u;
+ return arr != nullptr ? arr->size() : 0u;
}
} // namespace mirror
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 53fedab377..9d01a1db60 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -574,7 +574,7 @@ static ArtField* FindFieldByNameAndType(LengthPrefixedArray<ArtField>* fields,
return nullptr;
}
size_t low = 0;
- size_t high = fields->Length();
+ size_t high = fields->size();
ArtField* ret = nullptr;
while (low < high) {
size_t mid = (low + high) / 2;
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 90180c545b..5c12091ecb 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -95,6 +95,12 @@ inline bool Object::CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val) {
OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue());
}
+inline bool Object::CasLockWordWeakRelease(LockWord old_val, LockWord new_val) {
+ // Force use of non-transactional mode and do not check.
+ return CasFieldWeakRelease32<false, false>(
+ OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(), new_val.GetValue());
+}
+
inline uint32_t Object::GetLockOwnerThreadId() {
return Monitor::GetLockOwnerThreadId(this);
}
@@ -175,7 +181,10 @@ inline bool Object::AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object*
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(expected_rb_ptr)));
new_lw = lw;
new_lw.SetReadBarrierState(static_cast<uint32_t>(reinterpret_cast<uintptr_t>(rb_ptr)));
- } while (!CasLockWordWeakSequentiallyConsistent(expected_lw, new_lw));
+ // This CAS is a CAS release so that when GC updates all the fields of an object and then
+ // changes the object from gray to black, the field updates (stores) will be visible (won't be
+ // reordered after this CAS.)
+ } while (!CasLockWordWeakRelease(expected_lw, new_lw));
return true;
#elif USE_BROOKS_READ_BARRIER
DCHECK(kUseBrooksReadBarrier);
@@ -671,6 +680,24 @@ inline bool Object::CasFieldWeakRelaxed32(MemberOffset field_offset,
}
template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldWeakRelease32(MemberOffset field_offset,
+ int32_t old_value, int32_t new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteField32(this, field_offset, old_value, true);
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ AtomicInteger* atomic_addr = reinterpret_cast<AtomicInteger*>(raw_addr);
+
+ return atomic_addr->CompareExchangeWeakRelease(old_value, new_value);
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
inline bool Object::CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset,
int32_t old_value, int32_t new_value) {
if (kCheckTransaction) {
@@ -944,6 +971,62 @@ inline bool Object::CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrie
return success;
}
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldWeakRelaxedObjectWithoutWriteBarrier(
+ MemberOffset field_offset, Object* old_value, Object* new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(old_value);
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+ }
+ HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
+ HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_value));
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
+
+ bool success = atomic_addr->CompareExchangeWeakRelaxed(old_ref.reference_,
+ new_ref.reference_);
+ return success;
+}
+
+template<bool kTransactionActive, bool kCheckTransaction, VerifyObjectFlags kVerifyFlags>
+inline bool Object::CasFieldStrongRelaxedObjectWithoutWriteBarrier(
+ MemberOffset field_offset, Object* old_value, Object* new_value) {
+ if (kCheckTransaction) {
+ DCHECK_EQ(kTransactionActive, Runtime::Current()->IsActiveTransaction());
+ }
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ if (kVerifyFlags & kVerifyWrites) {
+ VerifyObject(new_value);
+ }
+ if (kVerifyFlags & kVerifyReads) {
+ VerifyObject(old_value);
+ }
+ if (kTransactionActive) {
+ Runtime::Current()->RecordWriteFieldReference(this, field_offset, old_value, true);
+ }
+ HeapReference<Object> old_ref(HeapReference<Object>::FromMirrorPtr(old_value));
+ HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_value));
+ uint8_t* raw_addr = reinterpret_cast<uint8_t*>(this) + field_offset.Int32Value();
+ Atomic<uint32_t>* atomic_addr = reinterpret_cast<Atomic<uint32_t>*>(raw_addr);
+
+ bool success = atomic_addr->CompareExchangeStrongRelaxed(old_ref.reference_,
+ new_ref.reference_);
+ return success;
+}
+
template<bool kIsStatic, typename Visitor>
inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& visitor) {
if (!kIsStatic && (ref_offsets != mirror::Class::kClassWalkSuper)) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index f75b8aeef4..5c6520fcab 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -99,7 +99,7 @@ class MANAGED LOCKABLE Object {
#ifndef USE_BAKER_OR_BROOKS_READ_BARRIER
NO_RETURN
#endif
- bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr)
+ ALWAYS_INLINE bool AtomicSetReadBarrierPointer(Object* expected_rb_ptr, Object* rb_ptr)
SHARED_REQUIRES(Locks::mutator_lock_);
void AssertReadBarrierPointer() const SHARED_REQUIRES(Locks::mutator_lock_);
@@ -135,6 +135,8 @@ class MANAGED LOCKABLE Object {
SHARED_REQUIRES(Locks::mutator_lock_);
bool CasLockWordWeakRelaxed(LockWord old_val, LockWord new_val)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool CasLockWordWeakRelease(LockWord old_val, LockWord new_val)
+ SHARED_REQUIRES(Locks::mutator_lock_);
uint32_t GetLockOwnerThreadId();
mirror::Object* MonitorEnter(Thread* self)
@@ -276,7 +278,6 @@ class MANAGED LOCKABLE Object {
Object* old_value,
Object* new_value)
SHARED_REQUIRES(Locks::mutator_lock_);
-
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CasFieldStrongSequentiallyConsistentObject(MemberOffset field_offset, Object* old_value,
@@ -288,6 +289,18 @@ class MANAGED LOCKABLE Object {
Object* old_value,
Object* new_value)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldWeakRelaxedObjectWithoutWriteBarrier(MemberOffset field_offset,
+ Object* old_value,
+ Object* new_value)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldStrongRelaxedObjectWithoutWriteBarrier(MemberOffset field_offset,
+ Object* old_value,
+ Object* new_value)
+ SHARED_REQUIRES(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
HeapReference<Object>* GetFieldObjectReferenceAddr(MemberOffset field_offset);
@@ -396,6 +409,12 @@ class MANAGED LOCKABLE Object {
template<bool kTransactionActive, bool kCheckTransaction = true,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool CasFieldWeakRelease32(MemberOffset field_offset, int32_t old_value,
+ int32_t new_value) ALWAYS_INLINE
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ template<bool kTransactionActive, bool kCheckTransaction = true,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool CasFieldStrongSequentiallyConsistent32(MemberOffset field_offset, int32_t old_value,
int32_t new_value) ALWAYS_INLINE
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 5b73557941..5337760fb8 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -270,7 +270,7 @@ inline MemberOffset ObjectArray<T>::OffsetOfElement(int32_t i) {
}
template<class T> template<typename Visitor>
-void ObjectArray<T>::VisitReferences(const Visitor& visitor) {
+inline void ObjectArray<T>::VisitReferences(const Visitor& visitor) {
const size_t length = static_cast<size_t>(GetLength());
for (size_t i = 0; i < length; ++i) {
visitor(this, OffsetOfElement(i), false);
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f5a04457e7..c1284a639f 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -307,10 +307,7 @@ TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
ScopedObjectAccess soa(Thread::Current());
Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", sizeof(void*));
- const DexFile::StringId* string_id = java_lang_dex_file_->FindStringId("[I");
- ASSERT_TRUE(string_id != nullptr);
- const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId(
- java_lang_dex_file_->GetIndexForStringId(*string_id));
+ const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I");
ASSERT_TRUE(type_id != nullptr);
uint32_t type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
Object* array = CheckAndAllocArrayFromCodeInstrumented(
@@ -367,16 +364,10 @@ TEST_F(ObjectTest, StaticFieldFromCode) {
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<ClassLoader*>(class_loader)));
Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", loader);
ArtMethod* clinit = klass->FindClassInitializer(sizeof(void*));
- const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;");
- ASSERT_TRUE(klass_string_id != nullptr);
- const DexFile::TypeId* klass_type_id = dex_file->FindTypeId(
- dex_file->GetIndexForStringId(*klass_string_id));
+ const DexFile::TypeId* klass_type_id = dex_file->FindTypeId("LStaticsFromCode;");
ASSERT_TRUE(klass_type_id != nullptr);
- const DexFile::StringId* type_string_id = dex_file->FindStringId("Ljava/lang/Object;");
- ASSERT_TRUE(type_string_id != nullptr);
- const DexFile::TypeId* type_type_id = dex_file->FindTypeId(
- dex_file->GetIndexForStringId(*type_string_id));
+ const DexFile::TypeId* type_type_id = dex_file->FindTypeId("Ljava/lang/Object;");
ASSERT_TRUE(type_type_id != nullptr);
const DexFile::StringId* name_str_id = dex_file->FindStringId("s0");
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 3a73900efa..5e423920c0 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -190,7 +190,7 @@ ALWAYS_INLINE static inline ArtField* FindFieldByName(
return nullptr;
}
size_t low = 0;
- size_t high = fields->Length();
+ size_t high = fields->size();
const uint16_t* const data = name->GetValue();
const size_t length = name->GetLength();
while (low < high) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 276e7f3ea5..5b780c38f8 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,7 +31,7 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '7', '2', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '7', '3', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index a162a4ea72..680f4ac027 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -547,6 +547,25 @@ bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {
return false;
}
const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
+
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "
+ "lookup table offset", GetLocation().c_str(), i,
+ dex_file_location.c_str());
+ return false;
+ }
+ uint32_t lookup_table_offset = *reinterpret_cast<const uint32_t*>(oat);
+ oat += sizeof(lookup_table_offset);
+ if (Begin() + lookup_table_offset > End()) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "
+ "lookup table", GetLocation().c_str(), i,
+ dex_file_location.c_str());
+ return false;
+ }
+ const uint8_t* lookup_table_data = lookup_table_offset != 0u
+ ? Begin() + lookup_table_offset
+ : nullptr;
+
const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);
oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
@@ -586,6 +605,7 @@ bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {
canonical_location,
dex_file_checksum,
dex_file_pointer,
+ lookup_table_data,
methods_offsets_pointer,
current_dex_cache_arrays);
oat_dex_files_storage_.push_back(oat_dex_file);
@@ -709,6 +729,7 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file,
const std::string& canonical_dex_file_location,
uint32_t dex_file_location_checksum,
const uint8_t* dex_file_pointer,
+ const uint8_t* lookup_table_data,
const uint32_t* oat_class_offsets_pointer,
uint8_t* dex_cache_arrays)
: oat_file_(oat_file),
@@ -716,6 +737,7 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file,
canonical_dex_file_location_(canonical_dex_file_location),
dex_file_location_checksum_(dex_file_location_checksum),
dex_file_pointer_(dex_file_pointer),
+ lookup_table_data_(lookup_table_data),
oat_class_offsets_pointer_(oat_class_offsets_pointer),
dex_cache_arrays_(dex_cache_arrays) {}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 6acdf86208..0a77654903 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -400,6 +400,10 @@ class OatDexFile FINAL {
return dex_cache_arrays_;
}
+ const uint8_t* GetLookupTableData() const {
+ return lookup_table_data_;
+ }
+
~OatDexFile();
private:
@@ -408,6 +412,7 @@ class OatDexFile FINAL {
const std::string& canonical_dex_file_location,
uint32_t dex_file_checksum,
const uint8_t* dex_file_pointer,
+ const uint8_t* lookup_table_data,
const uint32_t* oat_class_offsets_pointer,
uint8_t* dex_cache_arrays);
@@ -416,6 +421,7 @@ class OatDexFile FINAL {
const std::string canonical_dex_file_location_;
const uint32_t dex_file_location_checksum_;
const uint8_t* const dex_file_pointer_;
+ const uint8_t* lookup_table_data_;
const uint32_t* const oat_class_offsets_pointer_;
uint8_t* const dex_cache_arrays_;
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index 6eadd87d38..03cad0835e 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -21,6 +21,7 @@
#include "base/macros.h"
#include "quick/quick_method_frame_info.h"
#include "stack_map.h"
+#include "utils.h"
namespace art {
@@ -39,6 +40,18 @@ class PACKED(4) OatQuickMethodHeader {
~OatQuickMethodHeader();
+ static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) {
+ uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr);
+ uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_);
+ DCHECK(IsAlignedParam(code, GetInstructionSetAlignment(kRuntimeISA)) ||
+ IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)));
+ return reinterpret_cast<OatQuickMethodHeader*>(header);
+ }
+
+ static OatQuickMethodHeader* FromEntryPoint(const void* entry_point) {
+ return FromCodePointer(EntryPointToCodePointer(entry_point));
+ }
+
OatQuickMethodHeader& operator=(const OatQuickMethodHeader&) = default;
uintptr_t NativeQuickPcOffset(const uintptr_t pc) const {
@@ -74,6 +87,11 @@ class PACKED(4) OatQuickMethodHeader {
bool Contains(uintptr_t pc) const {
uintptr_t code_start = reinterpret_cast<uintptr_t>(code_);
+ static_assert(kRuntimeISA != kThumb2, "kThumb2 cannot be a runtime ISA");
+ if (kRuntimeISA == kArm) {
+ // On Thumb-2, the pc is offset by one.
+ code_start++;
+ }
return code_start <= pc && pc <= (code_start + code_size_);
}
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index bc9ba377bc..57472adb64 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -216,10 +216,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) {
LengthPrefixedArray<ArtField>* static_fields0 = proxyClass0->GetSFieldsPtr();
ASSERT_TRUE(static_fields0 != nullptr);
- ASSERT_EQ(2u, static_fields0->Length());
+ ASSERT_EQ(2u, static_fields0->size());
LengthPrefixedArray<ArtField>* static_fields1 = proxyClass1->GetSFieldsPtr();
ASSERT_TRUE(static_fields1 != nullptr);
- ASSERT_EQ(2u, static_fields1->Length());
+ ASSERT_EQ(2u, static_fields1->size());
EXPECT_EQ(static_fields0->At(0).GetDeclaringClass(), proxyClass0.Get());
EXPECT_EQ(static_fields0->At(1).GetDeclaringClass(), proxyClass0.Get());
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 99e262e6a9..65543942c7 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -73,7 +73,6 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) ==
bool InlineMethodAnalyser::AnalyseMethodCode(verifier::MethodVerifier* verifier,
InlineMethod* method) {
DCHECK(verifier != nullptr);
- DCHECK_EQ(Runtime::Current()->IsCompiler(), method != nullptr);
if (!Runtime::Current()->UseJit()) {
DCHECK_EQ(verifier->CanLoadClasses(), method != nullptr);
}
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 53b4f3a3b5..1552318c1e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -372,9 +372,14 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
StackMapEncoding encoding = code_info.ExtractEncoding();
StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
const size_t number_of_vregs = m->GetCodeItem()->registers_size_;
- DexRegisterMap vreg_map = code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs);
MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
uint32_t register_mask = stack_map.GetRegisterMask(encoding);
+ DexRegisterMap vreg_map = IsInInlinedFrame()
+ ? code_info.GetDexRegisterMapAtDepth(GetCurrentInliningDepth() - 1,
+ code_info.GetInlineInfoOf(stack_map, encoding),
+ encoding,
+ number_of_vregs)
+ : code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_vregs);
for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
if (updated_vregs != nullptr && updated_vregs[vreg]) {
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index 85ac4aab96..7de6c06f2b 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -19,7 +19,7 @@
#include "read_barrier.h"
-#include "gc/collector/concurrent_copying.h"
+#include "gc/collector/concurrent_copying-inl.h"
#include "gc/heap.h"
#include "mirror/object_reference.h"
#include "mirror/reference.h"
@@ -63,7 +63,7 @@ inline MirrorType* ReadBarrier::Barrier(
ref = reinterpret_cast<MirrorType*>(Mark(old_ref));
// Update the field atomically. This may fail if mutator updates before us, but it's ok.
if (ref != old_ref) {
- obj->CasFieldStrongSequentiallyConsistentObjectWithoutWriteBarrier<false, false>(
+ obj->CasFieldStrongRelaxedObjectWithoutWriteBarrier<false, false>(
offset, old_ref, ref);
}
}
@@ -101,7 +101,7 @@ inline MirrorType* ReadBarrier::BarrierForRoot(MirrorType** root,
// Update the field atomically. This may fail if mutator updates before us, but it's ok.
if (ref != old_ref) {
Atomic<mirror::Object*>* atomic_root = reinterpret_cast<Atomic<mirror::Object*>*>(root);
- atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, ref);
+ atomic_root->CompareExchangeStrongRelaxed(old_ref, ref);
}
}
AssertToSpaceInvariant(gc_root_source, ref);
@@ -140,7 +140,7 @@ inline MirrorType* ReadBarrier::BarrierForRoot(mirror::CompressedReference<Mirro
if (new_ref.AsMirrorPtr() != old_ref.AsMirrorPtr()) {
auto* atomic_root =
reinterpret_cast<Atomic<mirror::CompressedReference<MirrorType>>*>(root);
- atomic_root->CompareExchangeStrongSequentiallyConsistent(old_ref, new_ref);
+ atomic_root->CompareExchangeStrongRelaxed(old_ref, new_ref);
}
}
AssertToSpaceInvariant(gc_root_source, ref);
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 9359d27822..d7edfade15 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -856,13 +856,11 @@ static void AssertPcIsWithinQuickCode(ArtMethod* method, uintptr_t pc)
// If we are the JIT then we may have just compiled the method after the
// IsQuickToInterpreterBridge check.
jit::Jit* const jit = Runtime::Current()->GetJit();
- if (jit != nullptr &&
- jit->GetCodeCache()->ContainsCodePtr(reinterpret_cast<const void*>(code))) {
+ if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
return;
}
- uint32_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(
- EntryPointToCodePointer(code))[-1].code_size_;
+ uint32_t code_size = OatQuickMethodHeader::FromEntryPoint(code)->code_size_;
uintptr_t code_start = reinterpret_cast<uintptr_t>(code);
CHECK(code_start <= pc && pc <= (code_start + code_size))
<< PrettyMethod(method)
@@ -960,26 +958,18 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const {
return runtime->GetRuntimeMethodFrameInfo(method);
}
- // For Proxy method we add special handling for the direct method case (there is only one
- // direct method - constructor). Direct method is cloned from original
- // java.lang.reflect.Proxy class together with code and as a result it is executed as usual
- // quick compiled method without any stubs. So the frame info should be returned as it is a
- // quick method not a stub. However, if instrumentation stubs are installed, the
- // instrumentation->GetQuickCodeFor() returns the artQuickProxyInvokeHandler instead of an
- // oat code pointer, thus we have to add a special case here.
if (method->IsProxyMethod()) {
- if (method->IsDirect()) {
- CHECK(method->IsConstructor());
- const void* code_pointer =
- EntryPointToCodePointer(method->GetEntryPointFromQuickCompiledCode());
- return reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].frame_info_;
- } else {
- return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
- }
+ // There is only one direct method of a proxy class: the constructor. A direct method is
+ // cloned from the original java.lang.reflect.Proxy and is executed as usual quick
+ // compiled method without any stubs. Therefore the method must have a OatQuickMethodHeader.
+ DCHECK(!method->IsDirect() && !method->IsConstructor())
+ << "Constructors of proxy classes must have a OatQuickMethodHeader";
+ return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs);
}
- ClassLinker* class_linker = runtime->GetClassLinker();
+ // The only remaining case is if the method is native and uses the generic JNI stub.
DCHECK(method->IsNative());
+ ClassLinker* class_linker = runtime->GetClassLinker();
const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, sizeof(void*));
DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << PrettyMethod(method);
// Generic JNI frame.
diff --git a/runtime/stack.h b/runtime/stack.h
index 1276b244e7..aa7b6160fe 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -698,6 +698,10 @@ class StackVisitor {
return current_inlining_depth_ != 0;
}
+ size_t GetCurrentInliningDepth() const {
+ return current_inlining_depth_;
+ }
+
uintptr_t GetCurrentQuickFramePc() const {
return cur_quick_frame_pc_;
}
diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h
index a9da51ba29..ac04c3b403 100644
--- a/runtime/stride_iterator.h
+++ b/runtime/stride_iterator.h
@@ -19,6 +19,8 @@
#include <iterator>
+#include "base/logging.h"
+
namespace art {
template<typename T>
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index bdd5d1099c..b09b87fb58 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -60,8 +60,11 @@ static constexpr useconds_t kThreadSuspendMaxYieldUs = 3000;
static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000;
ThreadList::ThreadList()
- : suspend_all_count_(0), debug_suspend_all_count_(0), unregistering_count_(0),
- suspend_all_historam_("suspend all histogram", 16, 64), long_suspend_(false) {
+ : suspend_all_count_(0),
+ debug_suspend_all_count_(0),
+ unregistering_count_(0),
+ suspend_all_historam_("suspend all histogram", 16, 64),
+ long_suspend_(false) {
CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1, 0U)));
}
@@ -195,9 +198,7 @@ class DumpCheckpoint FINAL : public Closure {
MutexLock mu(self, *Locks::logging_lock_);
*os_ << local_os.str();
}
- if (thread->GetState() == kRunnable) {
- barrier_.Pass(self);
- }
+ barrier_.Pass(self);
}
void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
@@ -274,9 +275,6 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
Locks::thread_suspend_count_lock_->AssertNotHeld(self);
- if (kDebugLocking && gAborting == 0) {
- CHECK_NE(self->GetState(), kRunnable);
- }
std::vector<Thread*> suspended_count_modified_threads;
size_t count = 0;
@@ -285,12 +283,12 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
// manually called.
MutexLock mu(self, *Locks::thread_list_lock_);
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ count = list_.size();
for (const auto& thread : list_) {
if (thread != self) {
while (true) {
if (thread->RequestCheckpoint(checkpoint_function)) {
// This thread will run its checkpoint some time in the near future.
- count++;
break;
} else {
// We are probably suspended, try to make sure that we stay suspended.
@@ -383,7 +381,8 @@ size_t ThreadList::RunCheckpointOnRunnableThreads(Closure* checkpoint_function)
// from-space to to-space refs. Used to synchronize threads at a point
// to mark the initiation of marking while maintaining the to-space
// invariant.
-size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor,
+ Closure* flip_callback,
gc::collector::GarbageCollector* collector) {
TimingLogger::ScopedTiming split("ThreadListFlip", collector->GetTimings());
const uint64_t start_time = NanoTime();
@@ -511,7 +510,9 @@ void ThreadList::SuspendAll(const char* cause, bool long_suspend) {
// Debugger thread might be set to kRunnable for a short period of time after the
// SuspendAllInternal. This is safe because it will be set back to suspended state before
// the SuspendAll returns.
-void ThreadList::SuspendAllInternal(Thread* self, Thread* ignore1, Thread* ignore2,
+void ThreadList::SuspendAllInternal(Thread* self,
+ Thread* ignore1,
+ Thread* ignore2,
bool debug_suspend) {
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
@@ -700,12 +701,14 @@ void ThreadList::Resume(Thread* thread, bool for_debugger) {
VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") complete";
}
-static void ThreadSuspendByPeerWarning(Thread* self, LogSeverity severity, const char* message,
+static void ThreadSuspendByPeerWarning(Thread* self,
+ LogSeverity severity,
+ const char* message,
jobject peer) {
JNIEnvExt* env = self->GetJniEnv();
ScopedLocalRef<jstring>
- scoped_name_string(env, (jstring)env->GetObjectField(
- peer, WellKnownClasses::java_lang_Thread_name));
+ scoped_name_string(env, static_cast<jstring>(env->GetObjectField(
+ peer, WellKnownClasses::java_lang_Thread_name)));
ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
if (scoped_name_chars.c_str() == nullptr) {
LOG(severity) << message << ": " << peer;
@@ -715,8 +718,10 @@ static void ThreadSuspendByPeerWarning(Thread* self, LogSeverity severity, const
}
}
-Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
- bool debug_suspension, bool* timed_out) {
+Thread* ThreadList::SuspendThreadByPeer(jobject peer,
+ bool request_suspension,
+ bool debug_suspension,
+ bool* timed_out) {
const uint64_t start_time = NanoTime();
useconds_t sleep_us = kThreadSuspendInitialSleepUs;
*timed_out = false;
@@ -813,12 +818,14 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
}
}
-static void ThreadSuspendByThreadIdWarning(LogSeverity severity, const char* message,
+static void ThreadSuspendByThreadIdWarning(LogSeverity severity,
+ const char* message,
uint32_t thread_id) {
LOG(severity) << StringPrintf("%s: %d", message, thread_id);
}
-Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension,
+Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id,
+ bool debug_suspension,
bool* timed_out) {
const uint64_t start_time = NanoTime();
useconds_t sleep_us = kThreadSuspendInitialSleepUs;
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index c727432977..07ea10dbea 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -55,8 +55,8 @@ class ThreadList {
// Thread suspension support.
void ResumeAll()
- UNLOCK_FUNCTION(Locks::mutator_lock_)
- REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
+ REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
+ UNLOCK_FUNCTION(Locks::mutator_lock_);
void Resume(Thread* thread, bool for_debugger = false)
REQUIRES(!Locks::thread_suspend_count_lock_);
@@ -76,7 +76,8 @@ class ThreadList {
// is set to true.
Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension,
bool* timed_out)
- REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_,
+ REQUIRES(!Locks::mutator_lock_,
+ !Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
// Suspend a thread using its thread id, typically used by lock/monitor inflation. Returns the
@@ -84,14 +85,16 @@ class ThreadList {
// the thread terminating. Note that as thread ids are recycled this may not suspend the expected
// thread, that may be terminating. If the suspension times out then *timeout is set to true.
Thread* SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, bool* timed_out)
- REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_,
+ REQUIRES(!Locks::mutator_lock_,
+ !Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
// Find an already suspended thread (or self) by its id.
Thread* FindThreadByThreadId(uint32_t thin_lock_id);
// Run a checkpoint on threads, running threads are not suspended but run the checkpoint inside
- // of the suspend check. Returns how many checkpoints we should expect to run.
+ // of the suspend check. Returns how many checkpoints that are expected to run, including for
+ // already suspended threads for b/24191051.
size_t RunCheckpoint(Closure* checkpoint_function)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
@@ -100,14 +103,17 @@ class ThreadList {
// Flip thread roots from from-space refs to to-space refs. Used by
// the concurrent copying collector.
- size_t FlipThreadRoots(Closure* thread_flip_visitor, Closure* flip_callback,
+ size_t FlipThreadRoots(Closure* thread_flip_visitor,
+ Closure* flip_callback,
gc::collector::GarbageCollector* collector)
- REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_,
+ REQUIRES(!Locks::mutator_lock_,
+ !Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
// Suspends all threads
void SuspendAllForDebugger()
- REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_,
+ REQUIRES(!Locks::mutator_lock_,
+ !Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
void SuspendSelfForDebugger()
@@ -126,10 +132,14 @@ class ThreadList {
// Add/remove current thread from list.
void Register(Thread* self)
- REQUIRES(Locks::runtime_shutdown_lock_, !Locks::mutator_lock_, !Locks::thread_list_lock_,
+ REQUIRES(Locks::runtime_shutdown_lock_)
+ REQUIRES(!Locks::mutator_lock_,
+ !Locks::thread_list_lock_,
+ !Locks::thread_suspend_count_lock_);
+ void Unregister(Thread* self)
+ REQUIRES(!Locks::mutator_lock_,
+ !Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
- void Unregister(Thread* self) REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_,
- !Locks::thread_suspend_count_lock_);
void VisitRoots(RootVisitor* visitor) const
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -159,7 +169,9 @@ class ThreadList {
void WaitForOtherNonDaemonThreadsToExit()
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
- void SuspendAllInternal(Thread* self, Thread* ignore1, Thread* ignore2 = nullptr,
+ void SuspendAllInternal(Thread* self,
+ Thread* ignore1,
+ Thread* ignore2 = nullptr,
bool debug_suspend = false)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
@@ -200,8 +212,8 @@ class ScopedSuspendAll : public ValueObject {
!Locks::mutator_lock_);
// No REQUIRES(mutator_lock_) since the unlock function already asserts this.
~ScopedSuspendAll()
- UNLOCK_FUNCTION(Locks::mutator_lock_)
- REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
+ REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
+ UNLOCK_FUNCTION(Locks::mutator_lock_);
};
} // namespace art
diff --git a/runtime/type_lookup_table.cc b/runtime/type_lookup_table.cc
new file mode 100644
index 0000000000..0d40bb7be4
--- /dev/null
+++ b/runtime/type_lookup_table.cc
@@ -0,0 +1,131 @@
+/*
+ * 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 "type_lookup_table.h"
+
+#include "dex_file-inl.h"
+#include "utf-inl.h"
+#include "utils.h"
+
+#include <memory>
+#include <cstring>
+
+namespace art {
+
+static uint16_t MakeData(uint16_t class_def_idx, uint32_t hash, uint32_t mask) {
+ uint16_t hash_mask = static_cast<uint16_t>(~mask);
+ return (static_cast<uint16_t>(hash) & hash_mask) | class_def_idx;
+}
+
+TypeLookupTable::~TypeLookupTable() {
+ if (!owns_entries_) {
+ // We don't actually own the entries, don't let the unique_ptr release them.
+ entries_.release();
+ }
+}
+
+uint32_t TypeLookupTable::RawDataLength() const {
+ return RawDataLength(dex_file_);
+}
+
+uint32_t TypeLookupTable::RawDataLength(const DexFile& dex_file) {
+ return RoundUpToPowerOfTwo(dex_file.NumClassDefs()) * sizeof(Entry);
+}
+
+TypeLookupTable* TypeLookupTable::Create(const DexFile& dex_file) {
+ const uint32_t num_class_defs = dex_file.NumClassDefs();
+ return (num_class_defs == 0 || num_class_defs > std::numeric_limits<uint16_t>::max())
+ ? nullptr
+ : new TypeLookupTable(dex_file);
+}
+
+TypeLookupTable* TypeLookupTable::Open(const uint8_t* raw_data, const DexFile& dex_file) {
+ return new TypeLookupTable(raw_data, dex_file);
+}
+
+TypeLookupTable::TypeLookupTable(const DexFile& dex_file)
+ : dex_file_(dex_file),
+ mask_(RoundUpToPowerOfTwo(dex_file.NumClassDefs()) - 1),
+ entries_(new Entry[mask_ + 1]),
+ owns_entries_(true) {
+ std::vector<uint16_t> conflict_class_defs;
+ // The first stage. Put elements on their initial positions. If an initial position is already
+ // occupied then delay the insertion of the element to the second stage to reduce probing
+ // distance.
+ for (size_t i = 0; i < dex_file.NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(i);
+ const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_);
+ const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_);
+ const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id));
+ Entry entry;
+ entry.str_offset = str_id.string_data_off_;
+ entry.data = MakeData(i, hash, GetSizeMask());
+ if (!SetOnInitialPos(entry, hash)) {
+ conflict_class_defs.push_back(i);
+ }
+ }
+ // The second stage. The initial position of these elements had a collision. Put these elements
+ // into the nearest free cells and link them together by updating next_pos_delta.
+ for (uint16_t class_def_idx : conflict_class_defs) {
+ const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx);
+ const DexFile::TypeId& type_id = dex_file.GetTypeId(class_def.class_idx_);
+ const DexFile::StringId& str_id = dex_file.GetStringId(type_id.descriptor_idx_);
+ const uint32_t hash = ComputeModifiedUtf8Hash(dex_file.GetStringData(str_id));
+ Entry entry;
+ entry.str_offset = str_id.string_data_off_;
+ entry.data = MakeData(class_def_idx, hash, GetSizeMask());
+ Insert(entry, hash);
+ }
+}
+
+TypeLookupTable::TypeLookupTable(const uint8_t* raw_data, const DexFile& dex_file)
+ : dex_file_(dex_file),
+ mask_(RoundUpToPowerOfTwo(dex_file.NumClassDefs()) - 1),
+ entries_(reinterpret_cast<Entry*>(const_cast<uint8_t*>(raw_data))),
+ owns_entries_(false) {}
+
+bool TypeLookupTable::SetOnInitialPos(const Entry& entry, uint32_t hash) {
+ const uint32_t pos = hash & GetSizeMask();
+ if (!entries_[pos].IsEmpty()) {
+ return false;
+ }
+ entries_[pos] = entry;
+ entries_[pos].next_pos_delta = 0;
+ return true;
+}
+
+void TypeLookupTable::Insert(const Entry& entry, uint32_t hash) {
+ uint32_t pos = FindLastEntryInBucket(hash & GetSizeMask());
+ uint32_t next_pos = (pos + 1) & GetSizeMask();
+ while (!entries_[next_pos].IsEmpty()) {
+ next_pos = (next_pos + 1) & GetSizeMask();
+ }
+ const uint32_t delta = (next_pos >= pos) ? (next_pos - pos) : (next_pos + Size() - pos);
+ entries_[pos].next_pos_delta = delta;
+ entries_[next_pos] = entry;
+ entries_[next_pos].next_pos_delta = 0;
+}
+
+uint32_t TypeLookupTable::FindLastEntryInBucket(uint32_t pos) const {
+ const Entry* entry = &entries_[pos];
+ while (!entry->IsLast()) {
+ pos = (pos + entry->next_pos_delta) & GetSizeMask();
+ entry = &entries_[pos];
+ }
+ return pos;
+}
+
+} // namespace art
diff --git a/runtime/type_lookup_table.h b/runtime/type_lookup_table.h
new file mode 100644
index 0000000000..3c2295c428
--- /dev/null
+++ b/runtime/type_lookup_table.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_TYPE_LOOKUP_TABLE_H_
+#define ART_RUNTIME_TYPE_LOOKUP_TABLE_H_
+
+#include "dex_file.h"
+#include "leb128.h"
+#include "utf.h"
+
+namespace art {
+
+/**
+ * TypeLookupTable used to find class_def_idx by class descriptor quickly.
+ * Implementation of TypeLookupTable is based on hash table.
+ * This class instantiated at compile time by calling Create() method and written into OAT file.
+ * At runtime, the raw data is read from memory-mapped file by calling Open() method. The table
+ * memory remains clean.
+ */
+class TypeLookupTable {
+ public:
+ ~TypeLookupTable();
+
+ // Return the number of buckets in the lookup table.
+ uint32_t Size() const {
+ return mask_ + 1;
+ }
+
+ // Method search class_def_idx by class descriptor and it's hash.
+ // If no data found then the method returns DexFile::kDexNoIndex
+ ALWAYS_INLINE uint32_t Lookup(const char* str, uint32_t hash) const {
+ uint32_t pos = hash & GetSizeMask();
+ // Thanks to special insertion algorithm, the element at position pos can be empty or start of
+ // bucket.
+ const Entry* entry = &entries_[pos];
+ while (!entry->IsEmpty()) {
+ if (CmpHashBits(entry->data, hash) && IsStringsEquals(str, entry->str_offset)) {
+ return GetClassDefIdx(entry->data);
+ }
+ if (entry->IsLast()) {
+ return DexFile::kDexNoIndex;
+ }
+ pos = (pos + entry->next_pos_delta) & GetSizeMask();
+ entry = &entries_[pos];
+ }
+ return DexFile::kDexNoIndex;
+ }
+
+ // Method creates lookup table for dex file
+ static TypeLookupTable* Create(const DexFile& dex_file);
+
+ // Method opens lookup table from binary data. Lookup table does not owns binary data.
+ static TypeLookupTable* Open(const uint8_t* raw_data, const DexFile& dex_file);
+
+ // Method returns pointer to binary data of lookup table. Used by the oat writer.
+ const uint8_t* RawData() const {
+ return reinterpret_cast<const uint8_t*>(entries_.get());
+ }
+
+ // Method returns length of binary data. Used by the oat writer.
+ uint32_t RawDataLength() const;
+
+ // Method returns length of binary data for the specified dex file.
+ static uint32_t RawDataLength(const DexFile& dex_file);
+
+ private:
+ /**
+ * To find element we need to compare strings.
+ * It is faster to compare first hashes and then strings itself.
+ * But we have no full hash of element of table. But we can use 2 ideas.
+ * 1. All minor bits of hash inside one bucket are equals.
+ * 2. If dex file contains N classes and size of hash table is 2^n (where N <= 2^n)
+ * then 16-n bits are free. So we can encode part of element's hash into these bits.
+ * So hash of element can be divided on three parts:
+ * XXXX XXXX XXXX YYYY YZZZ ZZZZ ZZZZZ
+ * Z - a part of hash encoded in bucket (these bits of has are same for all elements in bucket) -
+ * n bits
+ * Y - a part of hash that we can write into free 16-n bits (because only n bits used to store
+ * class_def_idx)
+ * X - a part of has that we can't use without increasing increase
+ * So the data element of Entry used to store class_def_idx and part of hash of the entry.
+ */
+ struct Entry {
+ uint32_t str_offset;
+ uint16_t data;
+ uint16_t next_pos_delta;
+
+ Entry() : str_offset(0), data(0), next_pos_delta(0) {}
+
+ bool IsEmpty() const {
+ return str_offset == 0;
+ }
+
+ bool IsLast() const {
+ return next_pos_delta == 0;
+ }
+ };
+
+ // Construct from a dex file.
+ explicit TypeLookupTable(const DexFile& dex_file);
+
+ // Construct from a dex file with existing data.
+ TypeLookupTable(const uint8_t* raw_data, const DexFile& dex_file);
+
+ bool IsStringsEquals(const char* str, uint32_t str_offset) const {
+ const uint8_t* ptr = dex_file_.Begin() + str_offset;
+ // Skip string length.
+ DecodeUnsignedLeb128(&ptr);
+ return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
+ str, reinterpret_cast<const char*>(ptr)) == 0;
+ }
+
+ // Method extracts hash bits from element's data and compare them with
+ // the corresponding bits of the specified hash
+ bool CmpHashBits(uint32_t data, uint32_t hash) const {
+ uint32_t mask = static_cast<uint16_t>(~GetSizeMask());
+ return (hash & mask) == (data & mask);
+ }
+
+ uint32_t GetClassDefIdx(uint32_t data) const {
+ return data & mask_;
+ }
+
+ uint32_t GetSizeMask() const {
+ return mask_;
+ }
+
+ // Attempt to set an entry on it's hash' slot. If there is alrady something there, return false.
+ // Otherwise return true.
+ bool SetOnInitialPos(const Entry& entry, uint32_t hash);
+
+ // Insert an entry, probes until there is an empty slot.
+ void Insert(const Entry& entry, uint32_t hash);
+
+ // Find the last entry in a chain.
+ uint32_t FindLastEntryInBucket(uint32_t cur_pos) const;
+
+ const DexFile& dex_file_;
+ const uint32_t mask_;
+ std::unique_ptr<Entry[]> entries_;
+ // owns_entries_ specifies if the lookup table owns the entries_ array.
+ const bool owns_entries_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TypeLookupTable);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_TYPE_LOOKUP_TABLE_H_
diff --git a/runtime/type_lookup_table_test.cc b/runtime/type_lookup_table_test.cc
new file mode 100644
index 0000000000..7f500cc9a1
--- /dev/null
+++ b/runtime/type_lookup_table_test.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 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 <memory>
+
+#include "common_runtime_test.h"
+#include "dex_file-inl.h"
+#include "scoped_thread_state_change.h"
+#include "type_lookup_table.h"
+#include "utf-inl.h"
+
+namespace art {
+
+class TypeLookupTableTest : public CommonRuntimeTest {
+ public:
+ size_t kDexNoIndex = DexFile::kDexNoIndex; // Make copy to prevent linking errors.
+};
+
+TEST_F(TypeLookupTableTest, CreateLookupTable) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
+ std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
+ ASSERT_NE(nullptr, table.get());
+ ASSERT_NE(nullptr, table->RawData());
+ ASSERT_EQ(32U, table->RawDataLength());
+}
+
+TEST_F(TypeLookupTableTest, FindNonExistingClassWithoutCollisions) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
+ std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
+ ASSERT_NE(nullptr, table.get());
+ const char* descriptor = "LBA;";
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ uint32_t class_def_idx = table->Lookup(descriptor, hash);
+ ASSERT_EQ(kDexNoIndex, class_def_idx);
+}
+
+TEST_F(TypeLookupTableTest, FindNonExistingClassWithCollisions) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
+ std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
+ ASSERT_NE(nullptr, table.get());
+ const char* descriptor = "LDA;";
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ uint32_t class_def_idx = table->Lookup(descriptor, hash);
+ ASSERT_EQ(kDexNoIndex, class_def_idx);
+}
+
+TEST_F(TypeLookupTableTest, FindClassNoCollisions) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
+ std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
+ ASSERT_NE(nullptr, table.get());
+ const char* descriptor = "LC;";
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ uint32_t class_def_idx = table->Lookup(descriptor, hash);
+ ASSERT_EQ(2U, class_def_idx);
+}
+
+TEST_F(TypeLookupTableTest, FindClassWithCollisions) {
+ ScopedObjectAccess soa(Thread::Current());
+ std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Lookup"));
+ std::unique_ptr<TypeLookupTable> table(TypeLookupTable::Create(*dex_file));
+ ASSERT_NE(nullptr, table.get());
+ const char* descriptor = "LAB;";
+ size_t hash = ComputeModifiedUtf8Hash(descriptor);
+ uint32_t class_def_idx = table->Lookup(descriptor, hash);
+ ASSERT_EQ(1U, class_def_idx);
+}
+
+} // namespace art
diff --git a/runtime/utils.cc b/runtime/utils.cc
index dee4f9c891..48dce63f00 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1835,4 +1835,43 @@ void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostrea
os << "Something went wrong, didn't find the method in the class data.";
}
+static void ParseStringAfterChar(const std::string& s,
+ char c,
+ std::string* parsed_value,
+ UsageFn Usage) {
+ std::string::size_type colon = s.find(c);
+ if (colon == std::string::npos) {
+ Usage("Missing char %c in option %s\n", c, s.c_str());
+ }
+ // Add one to remove the char we were trimming until.
+ *parsed_value = s.substr(colon + 1);
+}
+
+void ParseDouble(const std::string& option,
+ char after_char,
+ double min,
+ double max,
+ double* parsed_value,
+ UsageFn Usage) {
+ std::string substring;
+ ParseStringAfterChar(option, after_char, &substring, Usage);
+ bool sane_val = true;
+ double value;
+ if ((false)) {
+ // TODO: this doesn't seem to work on the emulator. b/15114595
+ std::stringstream iss(substring);
+ iss >> value;
+ // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
+ sane_val = iss.eof() && (value >= min) && (value <= max);
+ } else {
+ char* end = nullptr;
+ value = strtod(substring.c_str(), &end);
+ sane_val = *end == '\0' && value >= min && value <= max;
+ }
+ if (!sane_val) {
+ Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
+ }
+ *parsed_value = value;
+}
+
} // namespace art
diff --git a/runtime/utils.h b/runtime/utils.h
index bd52b686fd..3690f86a80 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -26,8 +26,10 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/casts.h"
#include "base/logging.h"
#include "base/mutex.h"
+#include "base/stringpiece.h"
#include "globals.h"
#include "primitive.h"
@@ -35,7 +37,6 @@ class BacktraceMap;
namespace art {
-class ArtCode;
class ArtField;
class ArtMethod;
class DexFile;
@@ -321,6 +322,34 @@ static inline const void* EntryPointToCodePointer(const void* entry_point) {
return reinterpret_cast<const void*>(code);
}
+using UsageFn = void (*)(const char*, ...);
+
+template <typename T>
+static void ParseUintOption(const StringPiece& option,
+ const std::string& option_name,
+ T* out,
+ UsageFn Usage,
+ bool is_long_option = true) {
+ std::string option_prefix = option_name + (is_long_option ? "=" : "");
+ DCHECK(option.starts_with(option_prefix));
+ const char* value_string = option.substr(option_prefix.size()).data();
+ int64_t parsed_integer_value = 0;
+ if (!ParseInt(value_string, &parsed_integer_value)) {
+ Usage("Failed to parse %s '%s' as an integer", option_name.c_str(), value_string);
+ }
+ if (parsed_integer_value < 0) {
+ Usage("%s passed a negative value %d", option_name.c_str(), parsed_integer_value);
+ }
+ *out = dchecked_integral_cast<T>(parsed_integer_value);
+}
+
+void ParseDouble(const std::string& option,
+ char after_char,
+ double min,
+ double max,
+ double* parsed_value,
+ UsageFn Usage);
+
} // namespace art
#endif // ART_RUNTIME_UTILS_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index e1d4160aac..2db79ab229 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -665,20 +665,22 @@ bool MethodVerifier::Verify() {
// Interfaces may always have static initializers for their fields. If we are running with
// default methods enabled we also allow other public, static, non-final methods to have code.
// Otherwise that is the only type of method allowed.
- if (runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
- if (IsInstanceConstructor()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor";
- return false;
- } else if (method_access_flags_ & kAccFinal) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
- return false;
- } else if (!(method_access_flags_ & kAccPublic)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
+ if (!(IsConstructor() && IsStatic())) {
+ if (runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
+ if (IsInstanceConstructor()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-static constructor";
+ return false;
+ } else if (method_access_flags_ & kAccFinal) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods";
+ return false;
+ } else if (!(method_access_flags_ & kAccPublic)) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members";
+ return false;
+ }
+ } else {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
return false;
}
- } else if (!IsConstructor() || !IsStatic()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interface methods must be abstract";
- return false;
}
}
@@ -3662,8 +3664,15 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
<< PrettyMethod(res_method);
return nullptr;
}
- // Check that interface methods match interface classes.
- if (klass->IsInterface() && method_type != METHOD_INTERFACE) {
+ // Check that interface methods are static or match interface classes.
+ // We only allow statics if we don't have default methods enabled.
+ Runtime* runtime = Runtime::Current();
+ const bool default_methods_supported =
+ runtime == nullptr ||
+ runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods);
+ if (klass->IsInterface() &&
+ method_type != METHOD_INTERFACE &&
+ (!default_methods_supported || method_type != METHOD_STATIC)) {
Fail(VERIFY_ERROR_CLASS_CHANGE) << "non-interface method " << PrettyMethod(res_method)
<< " is in an interface class " << PrettyClass(klass);
return nullptr;
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 34fb3f8a01..2dbd7e8126 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -49,7 +49,9 @@ struct ReferenceMap2Visitor : public CheckReferenceMapVisitor {
if (m_name.compare("f") == 0) {
CHECK_REGS_CONTAIN_REFS(0x03U, true, 8); // v8: this
CHECK_REGS_CONTAIN_REFS(0x06U, true, 8, 1); // v8: this, v1: x
- CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1); // v8: this, v3: y, v1: x
+ if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
+ CHECK_REGS_CONTAIN_REFS(0x08U, true, 8, 3, 1); // v8: this, v3: y, v1: x
+ }
CHECK_REGS_CONTAIN_REFS(0x0cU, true, 8, 3, 1); // v8: this, v3: y, v1: x
if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
CHECK_REGS_CONTAIN_REFS(0x0eU, true, 8, 3, 1); // v8: this, v3: y, v1: x
@@ -66,9 +68,10 @@ struct ReferenceMap2Visitor : public CheckReferenceMapVisitor {
CHECK_REGS_CONTAIN_REFS(0x13U, false, 3); // v3: y
// Note that v0: ex can be eliminated because it's a dead merge of two different exceptions.
CHECK_REGS_CONTAIN_REFS(0x18U, true, 8, 2, 1); // v8: this, v2: y, v1: x (dead v0: ex)
- CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1); // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
if (!GetCurrentOatQuickMethodHeader()->IsOptimized()) {
// v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
+ CHECK_REGS_CONTAIN_REFS(0x1aU, true, 8, 5, 2, 1);
+ // v8: this, v5: x[1], v2: y, v1: x (dead v0: ex)
CHECK_REGS_CONTAIN_REFS(0x1dU, true, 8, 5, 2, 1);
// v5 is removed from the root set because there is a "merge" operation.
// See 0015: if-nez v2, 001f.
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 22829cddc8..ffeae7d9a2 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -624,12 +624,13 @@ public class Main {
constantIndexing2(new int[3]);
} catch (ArrayIndexOutOfBoundsException e) {
assertIsManaged(); // This is to ensure that single-frame deoptimization works.
- // Will need to be updated if constantIndexing2 is inlined.
+ // Will need to be updated if constantIndexing2 is inlined.
try {
// This will cause AIOOBE.
constantIndexingForward6(new int[3]);
} catch (ArrayIndexOutOfBoundsException e2) {
- assertIsManaged();
+ // Having deopted, we expect to be running interpreted at this point.
+ // Does not apply to debuggable, however, since we do not inline.
return 99;
}
}
diff --git a/test/538-checker-embed-constants/src/Main.java b/test/538-checker-embed-constants/src/Main.java
index d8618e30fb..979c4c86c0 100644
--- a/test/538-checker-embed-constants/src/Main.java
+++ b/test/538-checker-embed-constants/src/Main.java
@@ -260,6 +260,29 @@ public class Main {
return arg ^ 0xf00000000000000fL;
}
+ /**
+ * Test that the `-1` constant is not synthesized in a register and that we
+ * instead simply switch between `add` and `sub` instructions with the
+ * constant embedded.
+ * We need two uses (or more) of the constant because the compiler always
+ * defers to immediate value handling to VIXL when it has only one use.
+ */
+
+ /// CHECK-START-ARM64: long Main.addM1(long) register (after)
+ /// CHECK: <<Arg:j\d+>> ParameterValue
+ /// CHECK: <<ConstM1:j\d+>> LongConstant -1
+ /// CHECK-NOT: ParallelMove
+ /// CHECK: Add [<<Arg>>,<<ConstM1>>]
+ /// CHECK: Sub [<<Arg>>,<<ConstM1>>]
+
+ /// CHECK-START-ARM64: long Main.addM1(long) disassembly (after)
+ /// CHECK: sub x{{\d+}}, x{{\d+}}, #0x1
+ /// CHECK: add x{{\d+}}, x{{\d+}}, #0x1
+
+ public static long addM1(long arg) {
+ return (arg + (-1)) | (arg - (-1));
+ }
+
public static void main(String[] args) {
int arg = 0x87654321;
assertIntEquals(and255(arg), 0x21);
@@ -286,5 +309,7 @@ public class Main {
assertLongEquals(xorNot15(longArg), 0xedcba987789abcd1L);
assertLongEquals(xor0xfffffff00000000f(longArg), 0xedcba9888765432eL);
assertLongEquals(xor0xf00000000000000f(longArg), 0xe23456788765432eL);
+
+ assertLongEquals(14, addM1(7));
}
}
diff --git a/test/539-checker-arm64-encodable-immediates/info.txt b/test/539-checker-arm64-encodable-immediates/info.txt
deleted file mode 100644
index efeef33231..0000000000
--- a/test/539-checker-arm64-encodable-immediates/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Basic tests that check the compiler recognizes when constant values can be
-encoded in the immediate field of instructions.
diff --git a/test/539-checker-arm64-encodable-immediates/src/Main.java b/test/539-checker-arm64-encodable-immediates/src/Main.java
deleted file mode 100644
index 7e3ff9fde8..0000000000
--- a/test/539-checker-arm64-encodable-immediates/src/Main.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-public class Main {
-
- public static void assertLongEquals(long expected, long result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
-
- /**
- * Test that the `-1` constant is not synthesized in a register and that we
- * instead simply switch between `add` and `sub` instructions with the
- * constant embedded.
- * We need two uses (or more) of the constant because the compiler always
- * delegates the immediate value handling to VIXL when there is only one use.
- */
-
- /// CHECK-START-ARM64: long Main.addM1(long) register (after)
- /// CHECK: <<Arg:j\d+>> ParameterValue
- /// CHECK: <<ConstM1:j\d+>> LongConstant -1
- /// CHECK-NOT: ParallelMove
- /// CHECK: Add [<<Arg>>,<<ConstM1>>]
- /// CHECK: Sub [<<Arg>>,<<ConstM1>>]
-
- /// CHECK-START-ARM64: long Main.addM1(long) disassembly (after)
- /// CHECK: sub x{{\d+}}, x{{\d+}}, #0x1
- /// CHECK: add x{{\d+}}, x{{\d+}}, #0x1
-
- public static long addM1(long arg) {
- return (arg + (-1)) | (arg - (-1));
- }
-
- public static void main(String[] args) {
- assertLongEquals(14, addM1(7));
- }
-}
diff --git a/test/539-checker-arm64-encodable-immediates/expected.txt b/test/541-regression-inlined-deopt/expected.txt
index e69de29bb2..e69de29bb2 100644
--- a/test/539-checker-arm64-encodable-immediates/expected.txt
+++ b/test/541-regression-inlined-deopt/expected.txt
diff --git a/test/541-regression-inlined-deopt/info.txt b/test/541-regression-inlined-deopt/info.txt
new file mode 100644
index 0000000000..209588fe05
--- /dev/null
+++ b/test/541-regression-inlined-deopt/info.txt
@@ -0,0 +1,4 @@
+Regression test for deopt from optimized code which would use the top-level
+stack map for deopting inlined frames. Test case is written in smali for full
+control over vregs because the previous test 449 would pass because the vreg
+maps at the various inlining depths were similar.
diff --git a/test/541-regression-inlined-deopt/smali/TestCase.smali b/test/541-regression-inlined-deopt/smali/TestCase.smali
new file mode 100644
index 0000000000..a109775dfe
--- /dev/null
+++ b/test/541-regression-inlined-deopt/smali/TestCase.smali
@@ -0,0 +1,55 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LTestCase;
+.super Ljava/lang/Object;
+
+.method private static $inline$depth1([I)V
+ .registers 3
+
+ # Expects array in v2.
+
+ const v0, 0x0
+
+ const v1, 0x3
+ aput v0, p0, v1
+
+ const v1, 0x4
+ aput v0, p0, v1
+
+ return-void
+.end method
+
+.method private static $inline$depth0([I)V
+ .registers 1
+
+ # Expects array in v0.
+
+ invoke-static {p0}, LTestCase;->$inline$depth1([I)V
+ return-void
+.end method
+
+.method public static foo()V
+ .registers 10
+
+ # Create a new array short enough to throw AIOOB in $inline$depth1.
+ # Make sure the reference is not stored in the same vreg as used by
+ # the inlined methods.
+
+ const v5, 0x3
+ new-array v6, v5, [I
+
+ invoke-static {v6}, LTestCase;->$inline$depth0([I)V
+ return-void
+.end method
diff --git a/test/541-regression-inlined-deopt/src/Main.java b/test/541-regression-inlined-deopt/src/Main.java
new file mode 100644
index 0000000000..fa79590e65
--- /dev/null
+++ b/test/541-regression-inlined-deopt/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.*;
+
+public class Main {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Throwable {
+ try {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("foo");
+ m.invoke(null, (Object[]) null);
+ } catch (InvocationTargetException ex) {
+ // Code should have thrown AIOOB.
+ if (!(ex.getCause() instanceof ArrayIndexOutOfBoundsException)) {
+ throw ex;
+ }
+ }
+ }
+}
diff --git a/test/542-inline-trycatch/expected.txt b/test/542-inline-trycatch/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/542-inline-trycatch/expected.txt
diff --git a/test/542-inline-trycatch/info.txt b/test/542-inline-trycatch/info.txt
new file mode 100644
index 0000000000..b3e50d3d61
--- /dev/null
+++ b/test/542-inline-trycatch/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler under try/catch. \ No newline at end of file
diff --git a/test/542-inline-trycatch/src/Main.java b/test/542-inline-trycatch/src/Main.java
new file mode 100644
index 0000000000..5a6e06fa0c
--- /dev/null
+++ b/test/542-inline-trycatch/src/Main.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ // The following tests make sure that we inline methods used inside try and catch
+ // blocks, provided they meet other inlining criteria. To do that, we rely on
+ // the compiler recognizing and enforcing the $inline$ and $noinline$ markers.
+
+ // We expect a single block to always be inlined.
+
+ private static int $inline$SingleBlock(String str) throws NumberFormatException {
+ return Integer.parseInt(str);
+ }
+
+ // We expect a "simple" method with multiple blocks to always be inlined.
+
+ private static int $inline$MultipleBlocks(String str, boolean is_hex)
+ throws NumberFormatException {
+ return is_hex ? Integer.parseInt(str, 16) : Integer.parseInt(str);
+ }
+
+ // We expect methods with try/catch to not be inlined. Inlined try/catch
+ // blocks are not supported at the moment.
+
+ private static int $noinline$TryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ public static void testSingleBlockFromTry() {
+ int val = 0;
+
+ try {
+ val = $inline$SingleBlock("42");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ $inline$SingleBlock("xyz");
+ unreachable();
+ } catch (NumberFormatException ex) {}
+ }
+
+ public static void testSingleBlockFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$SingleBlock("42");
+ }
+ assertEquals(42, val);
+ }
+
+ public static void testMultipleBlocksFromTry() {
+ int val = 0;
+
+ try {
+ val = $inline$MultipleBlocks("42", false);
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ val = $inline$MultipleBlocks("20", true);
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(32, val);
+
+ try {
+ $inline$MultipleBlocks("xyz", false);
+ unreachable();
+ } catch (NumberFormatException ex) {}
+
+ try {
+ $inline$MultipleBlocks("xyz", true);
+ unreachable();
+ } catch (NumberFormatException ex) {}
+ }
+
+ public static void testMultipleBlocksFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$MultipleBlocks("42", false);
+ }
+ assertEquals(42, val);
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $inline$MultipleBlocks("20", true);
+ }
+ assertEquals(32, val);
+ }
+
+ public static void testTryCatchFromTry() {
+ int val = 0;
+
+ try {
+ val = $noinline$TryCatch("42");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(42, val);
+
+ try {
+ val = $noinline$TryCatch("xyz");
+ } catch (NumberFormatException ex) {
+ unreachable();
+ }
+ assertEquals(-1, val);
+ }
+
+ public static void testTryCatchFromCatch() {
+ int val = 0;
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $noinline$TryCatch("42");
+ }
+ assertEquals(42, val);
+
+ try {
+ throwException();
+ } catch (Exception ex) {
+ val = $noinline$TryCatch("xyz");
+ }
+ assertEquals(-1, val);
+ }
+
+ public static void main(String[] args) {
+ testSingleBlockFromTry();
+ testSingleBlockFromCatch();
+ testMultipleBlocksFromTry();
+ testMultipleBlocksFromCatch();
+ testTryCatchFromTry();
+ testTryCatchFromCatch();
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Wrong result: " + expected + " != " + actual);
+ }
+ }
+
+ private static void unreachable() {
+ throw new Error("Unreachable");
+ }
+
+ private static void throwException() throws Exception {
+ throw new Exception();
+ }
+}
diff --git a/test/542-unresolved-access-check/expected.txt b/test/542-unresolved-access-check/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/542-unresolved-access-check/expected.txt
diff --git a/test/542-unresolved-access-check/info.txt b/test/542-unresolved-access-check/info.txt
new file mode 100644
index 0000000000..30d45b8ec3
--- /dev/null
+++ b/test/542-unresolved-access-check/info.txt
@@ -0,0 +1 @@
+Test unresolved/access checks entry points with the JIT.
diff --git a/test/542-unresolved-access-check/src/Main.java b/test/542-unresolved-access-check/src/Main.java
new file mode 100644
index 0000000000..2bdf47f172
--- /dev/null
+++ b/test/542-unresolved-access-check/src/Main.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import p1.InP1;
+import p1.PlaceHolder;
+
+
+// Custom class loader to prevent loading while verifying.
+class MyClassLoader extends ClassLoader {
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
+
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
+
+ // Some magic to get access to the dexField field of pathList.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ dexElements = (Object[]) f.get(pathList);
+ dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
+ dexFileField.setAccessible(true);
+ }
+
+ Object[] dexElements;
+ Field dexFileField;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ if (className.equals("p1.OtherInP1") && !p1.PlaceHolder.entered) {
+ // The request comes from the verifier. Return null to get the access check entry
+ // point in the compiled code.
+ return null;
+ }
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object element : dexElements) {
+ Object dex = dexFileField.get(element);
+ Method method = dex.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dex != null) {
+ Class clazz = (Class)method.invoke(dex, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return getParent().loadClass(className);
+ }
+}
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ MyClassLoader o = new MyClassLoader();
+ Class foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("main");
+ m.invoke(null);
+ }
+}
+
+class LoadedByMyClassLoader {
+ public static void main() throws Exception {
+ for (int i = 0; i < 10000; ++i) {
+ // Warm up the JIT.
+ doTheCall(i);
+ }
+ // Sleep a while to let the JIT compile things.
+ // TODO(ngeoffray): Remove the sleep. b/25414532
+ Thread.sleep(2000);
+ doTheCall(10001);
+ }
+
+ public static void doTheCall(int i) {
+ InP1.$inline$AllocateOtherInP1(i);
+ InP1.$inline$AllocateArrayOtherInP1(i);
+ InP1.$inline$UseStaticFieldOtherInP1(i);
+ InP1.$inline$SetStaticFieldOtherInP1(i);
+ InP1.$inline$UseInstanceFieldOtherInP1(i);
+ InP1.$inline$SetInstanceFieldOtherInP1(i);
+ InP1.$inline$LoadOtherInP1(i);
+ InP1.$inline$StaticCallOtherInP1(i);
+ InP1.$inline$InstanceCallOtherInP1(i);
+ }
+}
diff --git a/test/542-unresolved-access-check/src/p1/InP1.java b/test/542-unresolved-access-check/src/p1/InP1.java
new file mode 100644
index 0000000000..3516c7231b
--- /dev/null
+++ b/test/542-unresolved-access-check/src/p1/InP1.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+public class InP1 {
+ public static Object $inline$AllocateOtherInP1(int i) {
+ // Let this method execute a while to make sure the JIT sees it hot.
+ if (i <= 10000) {
+ return null;
+ }
+ // Set the flag that we have entered InP1 code to get OtherInP1 loaded.
+ PlaceHolder.entered = true;
+ return new OtherInP1();
+ }
+
+ public static Object $inline$AllocateArrayOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return new OtherInP1[10];
+ }
+
+ public static Object $inline$UseStaticFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return OtherInP1.staticField;
+ }
+
+ public static void $inline$SetStaticFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return;
+ }
+ OtherInP1.staticField = new Object();
+ }
+
+ public static Object $inline$UseInstanceFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return $noinline$AllocateOtherInP1().instanceField;
+ }
+
+ public static void $inline$SetInstanceFieldOtherInP1(int i) {
+ if (i <= 10000) {
+ return;
+ }
+ $noinline$AllocateOtherInP1().instanceField = new Object();
+ }
+
+ public static OtherInP1 $noinline$AllocateOtherInP1() {
+ try {
+ return new OtherInP1();
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ public static Object $inline$LoadOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return OtherInP1.class;
+ }
+
+ public static Object $inline$StaticCallOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return OtherInP1.doTheStaticCall();
+ }
+
+ public static Object $inline$InstanceCallOtherInP1(int i) {
+ if (i <= 10000) {
+ return null;
+ }
+ return $noinline$AllocateOtherInP1().doTheInstanceCall();
+ }
+}
diff --git a/test/542-unresolved-access-check/src/p1/OtherInP1.java b/test/542-unresolved-access-check/src/p1/OtherInP1.java
new file mode 100644
index 0000000000..adc1ce1297
--- /dev/null
+++ b/test/542-unresolved-access-check/src/p1/OtherInP1.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+class OtherInP1 {
+ OtherInP1() {
+ }
+ static Object staticField = new Object();
+ Object instanceField = new Object();
+
+ static Object doTheStaticCall() {
+ return null;
+ }
+
+ Object doTheInstanceCall() {
+ return null;
+ }
+}
diff --git a/test/542-unresolved-access-check/src/p1/PlaceHolder.java b/test/542-unresolved-access-check/src/p1/PlaceHolder.java
new file mode 100644
index 0000000000..2bf4bdf15f
--- /dev/null
+++ b/test/542-unresolved-access-check/src/p1/PlaceHolder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package p1;
+
+// Specific class for putting the 'entered' marker. If we were to put the marker
+// in InP1 or in OtherInP1, the code in MyClassLoader using that marker would load
+// InP1 or OtherInP1 in the system class loader, and not in MyClassLoader.
+public class PlaceHolder {
+ public static boolean entered = false;
+}
diff --git a/test/955-lambda-smali/run b/test/955-lambda-smali/run
index b7546801b9..2fb2f89f6b 100755
--- a/test/955-lambda-smali/run
+++ b/test/955-lambda-smali/run
@@ -15,4 +15,4 @@
# limitations under the License.
# Ensure that the lambda experimental opcodes are turned on for dalvikvm and dex2oat
-${RUN} "$@" --runtime-option -Xexperimental:lambdas -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:lambdas
+${RUN} "$@" --experimental lambdas
diff --git a/test/960-default-smali/build b/test/960-default-smali/build
index c7866878e9..3946de3787 100755
--- a/test/960-default-smali/build
+++ b/test/960-default-smali/build
@@ -18,16 +18,25 @@
set -e
# Generate the smali Main.smali file or fail
-./util-src/generate_smali.py ./smali
+${ANDROID_BUILD_TOP}/art/test/utils/python/generate_smali_main.py ./smali
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
+USES_JAVA="false"
+if [[ $ARGS == *"--jvm"* ]]; then
+ USES_JAVA="true"
+elif [[ "$USE_JACK" == "true" ]]; then
+ if $JACK -D jack.java.source.version=1.8 >& /dev/null; then
+ USES_JAVA="true"
+ else
+ echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2
+ fi
+fi
+
+if [[ "$USES_JAVA" == "true" ]]; then
+ # We are compiling java code, create it.
mkdir -p src
- mkdir -p classes
${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
- ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+ # Ignore the smali directory.
+ EXTRA_ARGS="--no-smali"
fi
-# Build the smali files and make a dex
-${SMALI} -JXmx256m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
-zip "$TEST_NAME.jar" classes.dex
+./default-build "$@" "$EXTRA_ARGS" --experimental default-methods
diff --git a/test/960-default-smali/info.txt b/test/960-default-smali/info.txt
index eb596e2c9f..9583abbdc1 100644
--- a/test/960-default-smali/info.txt
+++ b/test/960-default-smali/info.txt
@@ -2,15 +2,16 @@ Smali-based tests for experimental interface default methods.
Obviously needs to run under ART or a Java 8 Language runtime and compiler.
-When run a Main.smali file will be generated by the util-src/generate_smali.py
-script. If we run with --jvm we will use the tools/extract-embedded-java script to
-turn the smali into equivalent Java using the embedded Java code.
+When run a Main.smali file will be generated by the
+test/utils/python/generate_smali_main.py script. If we run with --jvm we will
+use the tools/extract-embedded-java script to turn the smali into equivalent
+Java using the embedded Java code.
When updating be sure to write the equivalent Java code in comments of the smali
files.
-Care should be taken when updating the generate_smali.py script. It must always
-return equivalent output when run multiple times.
+Care should be taken when updating the generate_smali_main.py script. It must
+always return equivalent output when run multiple times.
To update the test files do the following steps:
<Add new classes/interfaces>
diff --git a/test/960-default-smali/run b/test/960-default-smali/run
index e378b061d9..22f68006e7 100755
--- a/test/960-default-smali/run
+++ b/test/960-default-smali/run
@@ -14,8 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if echo $@ | grep -q -- "--jvm"; then
- ${RUN} "$@"
-else
- ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
-fi
+${RUN} --experimental default-methods "$@"
diff --git a/test/961-default-iface-resolution-generated/build b/test/961-default-iface-resolution-generated/build
index 707c17e1cf..03cc62459a 100755
--- a/test/961-default-iface-resolution-generated/build
+++ b/test/961-default-iface-resolution-generated/build
@@ -17,8 +17,6 @@
# make us exit on a failure
set -e
-mkdir -p ./smali
-
# We will be making more files than the ulimit is set to allow. Remove it temporarily.
OLD_ULIMIT=`ulimit -S`
ulimit -S unlimited
@@ -28,20 +26,31 @@ restore_ulimit() {
}
trap 'restore_ulimit' ERR
+mkdir -p ./smali
+
# Generate the smali files and expected.txt or fail
./util-src/generate_smali.py ./smali ./expected.txt
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
+USES_JAVA="false"
+if [[ $ARGS == *"--jvm"* ]]; then
+ USES_JAVA="true"
+elif [[ $USE_JACK == "true" ]]; then
+ if "$JACK" -D jack.java.source.version=1.8 >& /dev/null; then
+ USES_JAVA="true"
+ else
+ echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2
+ fi
+fi
+
+if [[ "$USES_JAVA" == "true" ]]; then
+ # We are compiling java code, create it.
mkdir -p src
- mkdir -p classes
${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
- ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+ # Ignore the smali directory.
+ EXTRA_ARGS="--no-smali"
fi
-# Build the smali files and make a dex
-${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
-zip $TEST_NAME.jar classes.dex
+./default-build "$@" "$EXTRA_ARGS" --experimental default-methods
# Reset the ulimit back to its initial value
restore_ulimit
diff --git a/test/961-default-iface-resolution-generated/run b/test/961-default-iface-resolution-generated/run
index e378b061d9..22f68006e7 100755
--- a/test/961-default-iface-resolution-generated/run
+++ b/test/961-default-iface-resolution-generated/run
@@ -14,8 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if echo $@ | grep -q -- "--jvm"; then
- ${RUN} "$@"
-else
- ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
-fi
+${RUN} --experimental default-methods "$@"
diff --git a/test/962-iface-static/build b/test/962-iface-static/build
index 5ad82f70d1..24e2feb228 100755
--- a/test/962-iface-static/build
+++ b/test/962-iface-static/build
@@ -17,14 +17,23 @@
# make us exit on a failure
set -e
+USES_JAVA="false"
if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
+ USES_JAVA="true"
+elif [[ "$USE_JACK" == "true" ]]; then
+ if $JACK -D jack.java.source.version=1.8 2>/dev/null; then
+ USES_JAVA="true"
+ else
+ echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2
+ fi
+fi
+
+if [[ "$USES_JAVA" == "true" ]]; then
+ # We are compiling java code, create it.
mkdir -p src
- mkdir -p classes
${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
- ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+ # Ignore the smali directory.
+ EXTRA_ARGS="--no-smali"
fi
-# Build the smali files and make a dex
-${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
-zip $TEST_NAME.jar classes.dex
+./default-build "$@" "$EXTRA_ARGS" --experimental default-methods
diff --git a/test/962-iface-static/run b/test/962-iface-static/run
index e713708c18..d37737f3da 100755
--- a/test/962-iface-static/run
+++ b/test/962-iface-static/run
@@ -14,8 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if echo $@ | grep -q -- "--jvm"; then
- ${RUN} "$@"
-else
- ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
-fi
+${RUN} --experimental default-methods "$@"
diff --git a/test/962-iface-static/smali/Displayer.smali b/test/962-iface-static/smali/Displayer.smali
index 06bec16432..ed4c013d3b 100644
--- a/test/962-iface-static/smali/Displayer.smali
+++ b/test/962-iface-static/smali/Displayer.smali
@@ -27,7 +27,7 @@
.class public LDisplayer;
.super Ljava/lang/Object;
-.method public static <clinit>()V
+.method static constructor <clinit>()V
.locals 3
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v0, "init"
diff --git a/test/962-iface-static/smali/iface.smali b/test/962-iface-static/smali/iface.smali
index 441aae669e..5b9c03ec46 100644
--- a/test/962-iface-static/smali/iface.smali
+++ b/test/962-iface-static/smali/iface.smali
@@ -27,7 +27,7 @@
.field public final static f:LDisplayer;
-.method public static <clinit>()V
+.method static constructor <clinit>()V
.locals 3
new-instance v1, LDisplayer;
invoke-direct {v1}, LDisplayer;-><init>()V
diff --git a/test/963-default-range-smali/build b/test/963-default-range-smali/build
index 5ad82f70d1..24e2feb228 100755
--- a/test/963-default-range-smali/build
+++ b/test/963-default-range-smali/build
@@ -17,14 +17,23 @@
# make us exit on a failure
set -e
+USES_JAVA="false"
if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
+ USES_JAVA="true"
+elif [[ "$USE_JACK" == "true" ]]; then
+ if $JACK -D jack.java.source.version=1.8 2>/dev/null; then
+ USES_JAVA="true"
+ else
+ echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2
+ fi
+fi
+
+if [[ "$USES_JAVA" == "true" ]]; then
+ # We are compiling java code, create it.
mkdir -p src
- mkdir -p classes
${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
- ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+ # Ignore the smali directory.
+ EXTRA_ARGS="--no-smali"
fi
-# Build the smali files and make a dex
-${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
-zip $TEST_NAME.jar classes.dex
+./default-build "$@" "$EXTRA_ARGS" --experimental default-methods
diff --git a/test/963-default-range-smali/run b/test/963-default-range-smali/run
index e713708c18..d37737f3da 100755
--- a/test/963-default-range-smali/run
+++ b/test/963-default-range-smali/run
@@ -14,8 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if echo $@ | grep -q -- "--jvm"; then
- ${RUN} "$@"
-else
- ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
-fi
+${RUN} --experimental default-methods "$@"
diff --git a/test/964-default-iface-init-generated/build b/test/964-default-iface-init-generated/build
index deef803813..d916f1b8e9 100755
--- a/test/964-default-iface-init-generated/build
+++ b/test/964-default-iface-init-generated/build
@@ -29,17 +29,26 @@ trap 'restore_ulimit' ERR
# Generate the smali files and expected.txt or fail
./util-src/generate_smali.py ./smali ./expected.txt
+USES_JAVA="false"
if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
+ USES_JAVA="true"
+elif [[ "$USE_JACK" == "true" ]]; then
+ if $JACK -D jack.java.source.version=1.8 2>/dev/null; then
+ USES_JAVA="true"
+ else
+ echo "WARNING: Cannot use jack because it does not support JLS 1.8. Falling back to smali" >&2
+ fi
+fi
+
+if [[ "$USES_JAVA" == "true" ]]; then
+ # We are compiling java code, create it.
mkdir -p src
- mkdir -p classes
${ANDROID_BUILD_TOP}/art/tools/extract-embedded-java ./smali ./src
- ${JAVAC} -implicit:none -d classes $(find src -name '*.java')
+ # Ignore the smali directory.
+ EXTRA_ARGS="--no-smali"
fi
-# Build the smali files and make a dex
-${SMALI} -JXmx512m --experimental --api-level 23 --output classes.dex $(find smali -name '*.smali')
-zip $TEST_NAME.jar classes.dex
+./default-build "$@" "$EXTRA_ARGS" --experimental default-methods
# Reset the ulimit back to its initial value
restore_ulimit
diff --git a/test/964-default-iface-init-generated/run b/test/964-default-iface-init-generated/run
index e378b061d9..22f68006e7 100755
--- a/test/964-default-iface-init-generated/run
+++ b/test/964-default-iface-init-generated/run
@@ -14,8 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-if echo $@ | grep -q -- "--jvm"; then
- ${RUN} "$@"
-else
- ${RUN} "$@" --runtime-option -Xexperimental:default-methods -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:default-methods
-fi
+${RUN} --experimental default-methods "$@"
diff --git a/test/964-default-iface-init-generated/util-src/generate_smali.py b/test/964-default-iface-init-generated/util-src/generate_smali.py
index be2d3ba563..3c138abf26 100755
--- a/test/964-default-iface-init-generated/util-src/generate_smali.py
+++ b/test/964-default-iface-init-generated/util-src/generate_smali.py
@@ -334,7 +334,7 @@ class TestInterface(mixins.DumpMixin, mixins.Named, mixins.NameComparableMixin,
# public static final Displayer field = new Displayer("{tree}");
.field public final static field:LDisplayer;
-.method public static constructor <clinit>()V
+.method static constructor <clinit>()V
.locals 3
const-string v2, "{tree}"
new-instance v1, LDisplayer;
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 5bbbbc1f51..6ce3d9472c 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -441,11 +441,8 @@ TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
# Known broken tests for the mips32 optimizing compiler backend.
TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \
441-checker-inliner \
- 449-checker-bce \
510-checker-try-catch \
521-checker-array-set-null \
- 529-checker-unresolved \
- 534-checker-bce-deoptimization \
536-checker-intrinsic-optimization \
ifeq (mips,$(TARGET_ARCH))
diff --git a/test/Lookup/A.java b/test/Lookup/A.java
new file mode 100644
index 0000000000..666ba181d9
--- /dev/null
+++ b/test/Lookup/A.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class A {}
diff --git a/test/Lookup/AB.java b/test/Lookup/AB.java
new file mode 100644
index 0000000000..b231708111
--- /dev/null
+++ b/test/Lookup/AB.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class AB {}
diff --git a/test/Lookup/C.java b/test/Lookup/C.java
new file mode 100644
index 0000000000..5b90069262
--- /dev/null
+++ b/test/Lookup/C.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class C {}
diff --git a/test/etc/default-build b/test/etc/default-build
index c92402b529..7242428f1e 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -17,8 +17,45 @@
# Stop if something fails.
set -e
+# Set default values for directories.
+if [ -d smali ]; then
+ HAS_SMALI=true
+else
+ HAS_SMALI=false
+fi
+
+if [ -d src ]; then
+ HAS_SRC=true
+else
+ HAS_SRC=false
+fi
+
+if [ -d src2 ]; then
+ HAS_SRC2=true
+else
+ HAS_SRC2=false
+fi
+
+if [ -d src-multidex ]; then
+ HAS_SRC_MULTIDEX=true
+else
+ HAS_SRC_MULTIDEX=false
+fi
+
+if [ -d src-ex ]; then
+ HAS_SRC_EX=true
+else
+ HAS_SRC_EX=false
+fi
+
DX_FLAGS=""
SKIP_DX_MERGER="false"
+EXPERIMENTAL=""
+
+# Setup experimental flag mappings in a bash associative array.
+declare -A JACK_EXPERIMENTAL_ARGS
+JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8"
+JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8"
while true; do
if [ "x$1" = "x--dx-option" ]; then
@@ -28,6 +65,25 @@ while true; do
shift
elif [ "x$1" = "x--jvm" ]; then
shift
+ elif [ "x$1" = "x--no-src" ]; then
+ HAS_SRC=false
+ shift
+ elif [ "x$1" = "x--no-src2" ]; then
+ HAS_SRC2=false
+ shift
+ elif [ "x$1" = "x--no-src-multidex" ]; then
+ HAS_SRC_MULTIDEX=false
+ shift
+ elif [ "x$1" = "x--no-src-ex" ]; then
+ HAS_SRC_EX=false
+ shift
+ elif [ "x$1" = "x--no-smali" ]; then
+ HAS_SMALI=false
+ shift
+ elif [ "x$1" = "x--experimental" ]; then
+ shift
+ EXPERIMENTAL="${EXPERIMENTAL} $1"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -36,17 +92,22 @@ while true; do
fi
done
+# Add args from the experimental mappings.
+for experiment in ${EXPERIMENTAL}; do
+ JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}"
+done
+
if [ -e classes.dex ]; then
zip $TEST_NAME.jar classes.dex
exit 0
fi
-if ! [ -d src ] && ! [ -d src2 ]; then
+if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ]; then
# No src directory? Then forget about trying to run dx.
SKIP_DX_MERGER="true"
fi
-if [ -d src-multidex ]; then
+if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
# Jack does not support this configuration unless we specify how to partition the DEX file
# with a .jpp file.
USE_JACK="false"
@@ -54,27 +115,29 @@ fi
if [ ${USE_JACK} = "true" ]; then
# Jack toolchain
- if [ -d src ]; then
- ${JACK} --output-jack src.jack src
+ if [ "${HAS_SRC}" = "true" ]; then
+ ${JACK} ${JACK_ARGS} --output-jack src.jack src
imported_jack_files="--import src.jack"
fi
- if [ -d src2 ]; then
- ${JACK} --output-jack src2.jack src2
+ if [ "${HAS_SRC2}" = "true" ]; then
+ ${JACK} ${JACK_ARGS} --output-jack src2.jack src2
imported_jack_files="--import src2.jack ${imported_jack_files}"
fi
# Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider
# class definitions from src2 first.
- ${JACK} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex .
+ if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
+ ${JACK} ${JACK_ARGS} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex .
+ fi
else
# Legacy toolchain with javac+dx
- if [ -d src ]; then
+ if [ "${HAS_SRC}" = "true" ]; then
mkdir classes
- ${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
+ ${JAVAC} ${JAVAC_ARGS} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
fi
- if [ -d src-multidex ]; then
+ if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
mkdir classes2
${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
if [ ${NEED_DEX} = "true" ]; then
@@ -83,20 +146,22 @@ else
fi
fi
- if [ -d src2 ]; then
+ if [ "${HAS_SRC2}" = "true" ]; then
mkdir -p classes
- ${JAVAC} -d classes `find src2 -name '*.java'`
+ ${JAVAC} ${JAVAC_ARGS} -d classes `find src2 -name '*.java'`
fi
- if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
- --dump-width=1000 ${DX_FLAGS} classes
+ if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
+ if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
+ fi
fi
fi
-if [ -d smali ]; then
+if [ "${HAS_SMALI}" = "true" ]; then
# Compile Smali classes
- ${SMALI} -JXmx256m --experimental --api-level 23 --output smali_classes.dex `find smali -name '*.smali'`
+ ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
# Don't bother with dexmerger if we provide our own main function in a smali file.
if [ ${SKIP_DX_MERGER} = "false" ]; then
@@ -106,18 +171,18 @@ if [ -d smali ]; then
fi
fi
-if [ -d src-ex ]; then
+if [ ${HAS_SRC_EX} = "true" ]; then
if [ ${USE_JACK} = "true" ]; then
# Rename previous "classes.dex" so it is not overwritten.
mv classes.dex classes-1.dex
#TODO find another way to append src.jack to the jack classpath
- ${JACK}:src.jack --output-dex . src-ex
+ ${JACK}:src.jack ${JACK_ARGS} --output-dex . src-ex
zip $TEST_NAME-ex.jar classes.dex
# Restore previous "classes.dex" so it can be zipped.
mv classes-1.dex classes.dex
else
mkdir classes-ex
- ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
+ ${JAVAC} ${JAVAC_ARGS} -d classes-ex -cp classes `find src-ex -name '*.java'`
if [ ${NEED_DEX} = "true" ]; then
${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
--dump-width=1000 ${DX_FLAGS} classes-ex
@@ -133,7 +198,7 @@ if [ -d src-ex ]; then
fi
# Create a single jar with two dex files for multidex.
-if [ -d src-multidex ]; then
+if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex classes2.dex
elif [ ${NEED_DEX} = "true" ]; then
zip $TEST_NAME.jar classes.dex
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index fbefa073c4..18867fd035 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -18,6 +18,7 @@ DALVIKVM="dalvikvm32"
DEBUGGER="n"
DEV_MODE="n"
DEX2OAT=""
+EXPERIMENTAL=""
FALSE_BIN="/system/bin/false"
FLAGS=""
GDB=""
@@ -196,6 +197,13 @@ while true; do
FLAGS="${FLAGS} -Xcompiler-option --compile-pic"
COMPILE_FLAGS="${COMPILE_FLAGS} --compile-pic"
shift
+ elif [ "x$1" = "x--experimental" ]; then
+ if [ "$#" -lt 2 ]; then
+ echo "missing --experimental option" 1>&2
+ exit 1
+ fi
+ EXPERIMENTAL="$EXPERIMENTAL $2"
+ shift 2
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
exit 1
@@ -204,6 +212,13 @@ while true; do
fi
done
+if [ "$USE_JVM" = "n" ]; then
+ for feature in ${EXPERIMENTAL}; do
+ FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
+ done
+fi
+
if [ "x$1" = "x" ] ; then
MAIN="Main"
else
diff --git a/test/run-all-tests b/test/run-all-tests
index 76283b7a8d..6d5c28c7e0 100755
--- a/test/run-all-tests
+++ b/test/run-all-tests
@@ -44,12 +44,45 @@ while true; do
elif [ "x$1" = "x--use-java-home" ]; then
run_args="${run_args} --use-java-home"
shift
+ elif [ "x$1" = "x--no-image" ]; then
+ run_args="${run_args} --no-image"
+ shift
+ elif [ "x$1" = "x--quick" ]; then
+ run_args="${run_args} --quick"
+ shift
+ elif [ "x$1" = "x--optimizing" ]; then
+ run_args="${run_args} --optimizing"
+ shift
+ elif [ "x$1" = "x--image" ]; then
+ run_args="${run_args} --image"
+ shift
+ elif [ "x$1" = "x--never-clean" ]; then
+ run_args="${run_args} --never-clean"
+ shift
elif [ "x$1" = "x--jvm" ]; then
run_args="${run_args} --jvm"
shift
elif [ "x$1" = "x--debug" ]; then
run_args="${run_args} --debug"
shift
+ elif [ "x$1" = "x--build-only" ]; then
+ run_args="${run_args} --build-only"
+ shift
+ elif [ "x$1" = "x--build-with-jack" ]; then
+ run_args="${run_args} --build-with-jack"
+ shift
+ elif [ "x$1" = "x--build-with-javac-dx" ]; then
+ run_args="${run_args} --build-with-javac-dx"
+ shift
+ elif [ "x$1" = "x--dex2oat-swap" ]; then
+ run_args="${run_args} --dex2oat-swap"
+ shift
+ elif [ "x$1" = "x--dalvik" ]; then
+ run_args="${run_args} --dalvik"
+ shift
+ elif [ "x$1" = "x--debuggable" ]; then
+ run_args="${run_args} --debuggable"
+ shift
elif [ "x$1" = "x--zygote" ]; then
run_args="${run_args} --zygote"
shift
@@ -59,15 +92,15 @@ while true; do
elif [ "x$1" = "x--jit" ]; then
run_args="${run_args} --jit"
shift
+ elif [ "x$1" = "x--verify-soft-fail" ]; then
+ run_args="${run_args} --verify-soft-fail"
+ shift
elif [ "x$1" = "x--no-verify" ]; then
run_args="${run_args} --no-verify"
shift
elif [ "x$1" = "x--no-optimize" ]; then
run_args="${run_args} --no-optimize"
shift
- elif [ "x$1" = "x--valgrind" ]; then
- run_args="${run_args} --valgrind"
- shift
elif [ "x$1" = "x--dev" ]; then
run_args="${run_args} --dev"
shift
@@ -116,6 +149,15 @@ while true; do
elif [ "x$1" = "x--always-clean" ]; then
run_args="${run_args} --always-clean"
shift
+ elif [ "x$1" = "x--pic-test" ]; then
+ run_args="${run_args} --pic-test"
+ shift
+ elif [ "x$1" = "x--pic-image" ]; then
+ run_args="${run_args} --pic-image"
+ shift
+ elif [ "x$1" = "x--strace" ]; then
+ run_args="${run_args} --strace"
+ shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -134,9 +176,13 @@ if [ "$usage" = "yes" ]; then
echo " Options are all passed to run-test; refer to that for " \
"further documentation:"
echo " --debug --dev --host --interpreter --jit --jvm --no-optimize"
- echo " --no-verify -O --update --valgrind --zygote --64 --relocate"
- echo " --prebuild --always-clean --gcstress --gcverify --trace"
- echo " --no-patchoat --no-dex2oat --use-java-home"
+ echo " --no-verify --verify-soft-fail -O --update --zygote --64"
+ echo " --relocate --prebuild --always-clean --gcstress --gcverify"
+ echo " --trace --no-patchoat --no-dex2oat --use-java-home --pic-image"
+ echo " --pic-test --strace --debuggable --dalvik --dex2oat-swap"
+ echo " --build-only --build-with-jack --build-with-javac-dx"
+ echo " --never-clean --image --no-image --quick --optimizing"
+ echo " --no-relocate --no-prebuild"
echo " Specific Runtime Options:"
echo " --seq Run tests one-by-one, avoiding failures caused by busy CPU"
) 1>&2
diff --git a/test/run-test b/test/run-test
index 293779f7c3..f2bbaa7747 100755
--- a/test/run-test
+++ b/test/run-test
@@ -46,6 +46,7 @@ export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
export USE_JACK="false"
+export SMALI_ARGS="--experimental --api-level 23"
# If dx was not set by the environment variable, assume it is in the path.
if [ -z "$DX" ]; then
@@ -84,7 +85,7 @@ fi
# If JACK_CLASSPATH is not set, assume it only contains core-libart.
if [ -z "$JACK_CLASSPATH" ]; then
- export JACK_CLASSPATH="$ANDROID_BUILD_TOP/out/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack"
+ export JACK_CLASSPATH="${OUT_DIR:-$ANDROID_BUILD_TOP/out}/host/common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack"
fi
# If JILL_JAR is not set, assume it is located in the prebuilts directory.
@@ -439,7 +440,7 @@ elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
# ANDROID_HOST_OUT is not set in a build environment.
if [ -z "$ANDROID_HOST_OUT" ]; then
- export ANDROID_HOST_OUT=$ANDROID_BUILD_TOP/out/host/linux-x86
+ export ANDROID_HOST_OUT=${OUT_DIR:-$ANDROID_BUILD_TOP/out/}host/linux-x86
fi
guess_host_arch_name
run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}.art"
@@ -527,6 +528,7 @@ if [ "$usage" = "yes" ]; then
echo " --debug Wait for a debugger to attach."
echo " --debuggable Whether to compile Java code for a debugger."
echo " --gdb Run under gdb; incompatible with some tests."
+ echo " --gdb-arg Pass an option to gdb."
echo " --build-only Build test files only (off by default)."
echo " --build-with-javac-dx Build test files with javac and dx (on by default)."
echo " --build-with-jack Build test files with jack and jill (off by default)."
@@ -552,6 +554,8 @@ if [ "$usage" = "yes" ]; then
echo " the image and oat files be relocated to a random"
echo " address before running. (default)"
echo " --no-relocate Force the use of no relocating in the test"
+ echo " --image Run the test using a precompiled boot image. (default)"
+ echo " --no-image Run the test without a precompiled boot image."
echo " --host Use the host-mode virtual machine."
echo " --invoke-with Pass --invoke-with option to runtime."
echo " --dalvik Use Dalvik (off by default)."
@@ -563,6 +567,7 @@ if [ "$usage" = "yes" ]; then
"files."
echo " --64 Run the test in 64-bit mode"
echo " --trace Run with method tracing"
+ echo " --strace Run with syscall tracing from strace."
echo " --stream Run method tracing in streaming mode (requires --trace)"
echo " --gcstress Run with gc stress testing"
echo " --gcverify Run with gc verification"
@@ -572,6 +577,9 @@ if [ "$usage" = "yes" ]; then
echo " --dex2oat-swap Use a dex2oat swap file."
echo " --instruction-set-features [string]"
echo " Set instruction-set-features for compilation."
+ echo " --pic-image Use an image compiled with position independent code for the"
+ echo " boot class path."
+ echo " --pic-test Compile the test code position independent."
) 1>&2
exit 1
fi
diff --git a/test/960-default-smali/util-src/generate_smali.py b/test/utils/python/generate_smali_main.py
index b2bf1f0761..d796d313c6 100755
--- a/test/960-default-smali/util-src/generate_smali.py
+++ b/test/utils/python/generate_smali_main.py
@@ -15,7 +15,7 @@
# limitations under the License.
"""
-Generate Smali Main file for test 960
+Generate Smali Main file from a classes.xml file.
"""
import os
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 81ea79a6e2..9a8b462740 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -159,13 +159,6 @@
bug: 22786792
},
{
- description: "Formatting failures",
- result: EXEC_FAILED,
- names: ["libcore.java.text.NumberFormatTest#test_currencyFromLocale",
- "libcore.java.text.NumberFormatTest#test_currencyWithPatternDigits"],
- bug: 25136848
-},
-{
description: "Lack of IPv6 on some buildbot slaves",
result: EXEC_FAILED,
names: ["libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6",
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index edec362025..de27a6faaa 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -30,8 +30,6 @@ fi
art="/data/local/tmp/system/bin/art"
art_debugee="sh /data/local/tmp/system/bin/art"
-# We use Quick's image on target because optimizing's image is not compiled debuggable.
-image="-Ximage:/data/art-test/core.art"
args=$@
debuggee_args="-Xcompiler-option --debuggable"
device_dir="--device-dir=/data/local/tmp"
@@ -41,6 +39,8 @@ vm_command="--vm-command=$art"
image_compiler_option=""
debug="no"
verbose="no"
+image="-Ximage:/data/art-test/core-jit.art"
+vm_args=""
# By default, we run the whole JDWP test suite.
test="org.apache.harmony.jpda.tests.share.AllTests"
@@ -88,7 +88,11 @@ while true; do
fi
done
-vm_args="--vm-arg $image"
+if [[ "$image" != "" ]]; then
+ vm_args="--vm-arg $image"
+fi
+vm_args="$vm_args --vm-arg -Xusejit:true"
+debuggee_args="$debuggee_args -Xusejit:true"
if [[ $debug == "yes" ]]; then
art="$art -d"
art_debugee="$art_debugee -d"
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 80f7a3737f..4b5a5ca76d 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -57,7 +57,6 @@ working_packages=("dalvik.system"
"org.apache.harmony.luni"
"org.apache.harmony.nio"
"org.apache.harmony.regex"
- "org.apache.harmony.security"
"org.apache.harmony.testframework"
"org.apache.harmony.tests.java.io"
"org.apache.harmony.tests.java.lang"
@@ -68,6 +67,10 @@ working_packages=("dalvik.system"
"tests.java.lang.String"
"jsr166")
+# List of packages we could run, but don't have rights to revert
+# changes in case of failures.
+# "org.apache.harmony.security"
+
vogar_args=$@
while true; do
if [[ "$1" == "--mode=device" ]]; then
@@ -102,4 +105,4 @@ vogar_args="$vogar_args --timeout 480"
# Run the tests using vogar.
echo "Running tests for the following test packages:"
echo ${working_packages[@]} | tr " " "\n"
-vogar $vogar_args --expectations art/tools/libcore_failures.txt --classpath $jsr166_test_jar --classpath $test_jar ${working_packages[@]}
+vogar $vogar_args --vm-arg -Xusejit:true --expectations art/tools/libcore_failures.txt --classpath $jsr166_test_jar --classpath $test_jar ${working_packages[@]}
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 7faf86ed5c..d5b89897e5 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -30,3 +30,10 @@ adb shell ifconfig
echo -e "${green}List properties${nc}"
adb shell getprop
+
+echo -e "${green}Uptime${nc}"
+adb shell uptime
+
+echo -e "${green}Kill stalled dalvikvm processes${nc}"
+processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
+for i in $processes; do adb shell kill -9 $i; done