summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2013-11-06 01:33:15 -0800
committerBrian Carlstrom <bdc@google.com>2013-11-06 01:33:15 -0800
commit8e16efc49eda5ea8c13da577cb2e9f625f659f1f (patch)
tree533cf9a77046a49525d916043a58949e59319f2d
parentf50a0e17ae5443bed0f80d628f71e5effc2faf07 (diff)
parent7020278bce98a0735dc6abcbd33bdf1ed2634f1d (diff)
downloadart-8e16efc49eda5ea8c13da577cb2e9f625f659f1f.tar.gz
art-8e16efc49eda5ea8c13da577cb2e9f625f659f1f.tar.bz2
art-8e16efc49eda5ea8c13da577cb2e9f625f659f1f.zip
Merge remote-tracking branch 'goog/dalvik-dev' into merge-art
-rw-r--r--Android.mk3
-rw-r--r--build/Android.common.mk1
-rw-r--r--build/Android.executable.mk4
-rw-r--r--build/Android.gtest.mk3
-rw-r--r--build/Android.oat.mk22
-rw-r--r--compiler/Android.mk1
-rw-r--r--compiler/dex/arena_allocator.cc36
-rw-r--r--compiler/dex/arena_allocator.h9
-rw-r--r--compiler/dex/arena_allocator_test.cc33
-rw-r--r--compiler/dex/arena_bit_vector.cc130
-rw-r--r--compiler/dex/arena_bit_vector.h99
-rw-r--r--compiler/dex/compiler_enums.h26
-rw-r--r--compiler/dex/compiler_ir.h24
-rw-r--r--compiler/dex/dataflow_iterator-inl.h71
-rw-r--r--compiler/dex/dataflow_iterator.h126
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc15
-rw-r--r--compiler/dex/frontend.cc39
-rw-r--r--compiler/dex/frontend.h1
-rw-r--r--compiler/dex/growable_array.h10
-rw-r--r--compiler/dex/mir_analysis.cc21
-rw-r--r--compiler/dex/mir_dataflow.cc31
-rw-r--r--compiler/dex/mir_graph.cc299
-rw-r--r--compiler/dex/mir_graph.h192
-rw-r--r--compiler/dex/mir_optimization.cc192
-rw-r--r--compiler/dex/portable/mir_to_gbc.cc70
-rw-r--r--compiler/dex/quick/arm/arm_lir.h10
-rw-r--r--compiler/dex/quick/arm/assemble_arm.cc1425
-rw-r--r--compiler/dex/quick/arm/call_arm.cc199
-rw-r--r--compiler/dex/quick/arm/codegen_arm.h26
-rw-r--r--compiler/dex/quick/arm/fp_arm.cc2
-rw-r--r--compiler/dex/quick/arm/int_arm.cc212
-rw-r--r--compiler/dex/quick/arm/target_arm.cc131
-rw-r--r--compiler/dex/quick/arm/utility_arm.cc74
-rw-r--r--compiler/dex/quick/codegen_util.cc295
-rw-r--r--compiler/dex/quick/gen_common.cc112
-rw-r--r--compiler/dex/quick/gen_invoke.cc225
-rw-r--r--compiler/dex/quick/local_optimizations.cc102
-rw-r--r--compiler/dex/quick/mips/assemble_mips.cc148
-rw-r--r--compiler/dex/quick/mips/call_mips.cc54
-rw-r--r--compiler/dex/quick/mips/codegen_mips.h18
-rw-r--r--compiler/dex/quick/mips/int_mips.cc101
-rw-r--r--compiler/dex/quick/mips/target_mips.cc25
-rw-r--r--compiler/dex/quick/mips/utility_mips.cc2
-rw-r--r--compiler/dex/quick/mir_to_lir-inl.h73
-rw-r--r--compiler/dex/quick/mir_to_lir.cc108
-rw-r--r--compiler/dex/quick/mir_to_lir.h208
-rw-r--r--compiler/dex/quick/ralloc_util.cc217
-rw-r--r--compiler/dex/quick/x86/assemble_x86.cc188
-rw-r--r--compiler/dex/quick/x86/call_x86.cc58
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h25
-rw-r--r--compiler/dex/quick/x86/fp_x86.cc4
-rw-r--r--compiler/dex/quick/x86/int_x86.cc100
-rw-r--r--compiler/dex/quick/x86/target_x86.cc37
-rw-r--r--compiler/dex/quick/x86/utility_x86.cc8
-rw-r--r--compiler/dex/quick/x86/x86_lir.h4
-rw-r--r--compiler/dex/ssa_transformation.cc168
-rw-r--r--compiler/dex/vreg_analysis.cc201
-rw-r--r--compiler/driver/compiler_driver.cc362
-rw-r--r--compiler/driver/compiler_driver.h74
-rw-r--r--compiler/elf_fixup.cc5
-rw-r--r--compiler/elf_stripper.cc11
-rw-r--r--compiler/elf_stripper.h4
-rw-r--r--compiler/elf_writer.cc5
-rw-r--r--compiler/elf_writer_mclinker.cc10
-rw-r--r--compiler/elf_writer_test.cc17
-rw-r--r--compiler/image_test.cc11
-rw-r--r--compiler/image_writer.cc60
-rw-r--r--compiler/image_writer.h5
-rw-r--r--compiler/jni/jni_compiler_test.cc4
-rw-r--r--compiler/jni/portable/jni_compiler.cc4
-rw-r--r--compiler/jni/portable/jni_compiler.h4
-rw-r--r--compiler/jni/quick/jni_compiler.cc11
-rw-r--r--compiler/llvm/compiler_llvm.cc3
-rw-r--r--compiler/llvm/gbc_expander.cc20
-rw-r--r--compiler/llvm/llvm_compilation_unit.cc9
-rw-r--r--compiler/llvm/runtime_support_builder.cc76
-rw-r--r--compiler/llvm/runtime_support_builder.h4
-rw-r--r--compiler/llvm/runtime_support_builder_arm.cc19
-rw-r--r--compiler/llvm/runtime_support_builder_arm.h4
-rw-r--r--compiler/llvm/runtime_support_builder_thumb2.cc90
-rw-r--r--compiler/oat_test.cc37
-rw-r--r--compiler/oat_writer.cc250
-rw-r--r--compiler/oat_writer.h64
-rw-r--r--compiler/utils/dedupe_set.h58
-rw-r--r--compiler/utils/dedupe_set_test.cc11
-rw-r--r--dalvikvm/Android.mk2
-rw-r--r--dex2oat/dex2oat.cc116
-rw-r--r--disassembler/Android.mk120
-rw-r--r--disassembler/disassembler.cc (renamed from runtime/disassembler.cc)0
-rw-r--r--disassembler/disassembler.h (renamed from runtime/disassembler.h)6
-rw-r--r--disassembler/disassembler_arm.cc (renamed from runtime/disassembler_arm.cc)297
-rw-r--r--disassembler/disassembler_arm.h (renamed from runtime/disassembler_arm.h)6
-rw-r--r--disassembler/disassembler_mips.cc (renamed from runtime/disassembler_mips.cc)0
-rw-r--r--disassembler/disassembler_mips.h (renamed from runtime/disassembler_mips.h)6
-rw-r--r--disassembler/disassembler_x86.cc (renamed from runtime/disassembler_x86.cc)4
-rw-r--r--disassembler/disassembler_x86.h (renamed from runtime/disassembler_x86.h)6
-rw-r--r--oatdump/Android.mk8
-rw-r--r--oatdump/oatdump.cc50
-rw-r--r--runtime/Android.mk12
-rw-r--r--runtime/arch/arm/arm_sdiv.S24
-rw-r--r--runtime/arch/arm/asm_support_arm.h4
-rw-r--r--runtime/arch/arm/entrypoints_init_arm.cc56
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S299
-rw-r--r--runtime/arch/arm/thread_arm.cc2
-rw-r--r--runtime/arch/mips/asm_support_mips.h2
-rw-r--r--runtime/arch/mips/entrypoints_init_mips.cc66
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S263
-rw-r--r--runtime/arch/mips/thread_mips.cc1
-rw-r--r--runtime/arch/x86/asm_support_x86.h4
-rw-r--r--runtime/arch/x86/entrypoints_init_x86.cc58
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S240
-rw-r--r--runtime/arch/x86/thread_x86.cc2
-rw-r--r--runtime/asm_support.h14
-rw-r--r--runtime/base/allocator.cc74
-rw-r--r--runtime/base/allocator.h (renamed from compiler/llvm/runtime_support_builder_thumb2.h)28
-rw-r--r--runtime/base/bit_vector.cc177
-rw-r--r--runtime/base/bit_vector.h134
-rw-r--r--runtime/base/bit_vector_test.cc122
-rw-r--r--runtime/base/macros.h6
-rw-r--r--runtime/base/mutex-inl.h126
-rw-r--r--runtime/base/mutex.cc159
-rw-r--r--runtime/base/mutex.h2
-rw-r--r--runtime/base/unix_file/fd_file.cc4
-rw-r--r--runtime/base/unix_file/fd_file.h4
-rw-r--r--runtime/class_linker.cc648
-rw-r--r--runtime/class_linker.h85
-rw-r--r--runtime/class_linker_test.cc33
-rw-r--r--runtime/common_test.h153
-rw-r--r--runtime/common_throws.cc11
-rw-r--r--runtime/common_throws.h81
-rw-r--r--runtime/debugger.cc39
-rw-r--r--runtime/debugger.h2
-rw-r--r--runtime/dex_file-inl.h76
-rw-r--r--runtime/dex_file.cc231
-rw-r--r--runtime/dex_file.h107
-rw-r--r--runtime/dex_file_test.cc42
-rw-r--r--runtime/dex_file_verifier.cc548
-rw-r--r--runtime/dex_file_verifier.h42
-rw-r--r--runtime/dex_instruction-inl.h117
-rw-r--r--runtime/dex_instruction.h183
-rw-r--r--runtime/dex_method_iterator_test.cc23
-rw-r--r--runtime/elf_file.cc63
-rw-r--r--runtime/elf_file.h6
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc240
-rw-r--r--runtime/entrypoints/entrypoint_utils.h385
-rw-r--r--runtime/entrypoints/interpreter/interpreter_entrypoints.cc12
-rw-r--r--runtime/entrypoints/jni/jni_entrypoints.cc4
-rw-r--r--runtime/entrypoints/math_entrypoints.cc50
-rw-r--r--runtime/entrypoints/portable/portable_entrypoints.h1
-rw-r--r--runtime/entrypoints/portable/portable_field_entrypoints.cc56
-rw-r--r--runtime/entrypoints/portable/portable_invoke_entrypoints.cc52
-rw-r--r--runtime/entrypoints/quick/quick_alloc_entrypoints.cc53
-rw-r--r--runtime/entrypoints/quick/quick_cast_entrypoints.cc37
-rw-r--r--runtime/entrypoints/quick/quick_entrypoints.h23
-rw-r--r--runtime/entrypoints/quick/quick_field_entrypoints.cc39
-rw-r--r--runtime/entrypoints/quick/quick_invoke_entrypoints.cc42
-rw-r--r--runtime/entrypoints/quick/quick_jni_entrypoints.cc30
-rw-r--r--runtime/entrypoints/quick/quick_lock_entrypoints.cc42
-rw-r--r--runtime/entrypoints/quick/quick_math_entrypoints.cc6
-rw-r--r--runtime/entrypoints/quick/quick_throw_entrypoints.cc20
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc37
-rw-r--r--runtime/exception_test.cc2
-rw-r--r--runtime/gc/accounting/atomic_stack.h6
-rw-r--r--runtime/gc/accounting/card_table.cc6
-rw-r--r--runtime/gc/accounting/gc_allocator.cc18
-rw-r--r--runtime/gc/accounting/gc_allocator.h83
-rw-r--r--runtime/gc/accounting/heap_bitmap.h4
-rw-r--r--runtime/gc/accounting/mod_union_table-inl.h14
-rw-r--r--runtime/gc/accounting/mod_union_table.cc146
-rw-r--r--runtime/gc/accounting/mod_union_table.h57
-rw-r--r--runtime/gc/accounting/space_bitmap.cc8
-rw-r--r--runtime/gc/accounting/space_bitmap.h6
-rw-r--r--runtime/gc/allocator/dlmalloc.cc16
-rw-r--r--runtime/gc/allocator/dlmalloc.h8
-rw-r--r--runtime/gc/collector/mark_sweep-inl.h27
-rw-r--r--runtime/gc/collector/mark_sweep.cc150
-rw-r--r--runtime/gc/collector/mark_sweep.h54
-rw-r--r--runtime/gc/collector/sticky_mark_sweep.h4
-rw-r--r--runtime/gc/heap-inl.h188
-rw-r--r--runtime/gc/heap.cc295
-rw-r--r--runtime/gc/heap.h92
-rw-r--r--runtime/gc/space/dlmalloc_space-inl.h8
-rw-r--r--runtime/gc/space/dlmalloc_space.cc40
-rw-r--r--runtime/gc/space/dlmalloc_space.h27
-rw-r--r--runtime/gc/space/image_space.cc134
-rw-r--r--runtime/gc/space/image_space.h11
-rw-r--r--runtime/gc/space/large_object_space.cc15
-rw-r--r--runtime/gc/space/large_object_space.h14
-rw-r--r--runtime/gc/space/space.h8
-rw-r--r--runtime/globals.h20
-rw-r--r--runtime/hprof/hprof.cc8
-rw-r--r--runtime/image.h2
-rw-r--r--runtime/indirect_reference_table.cc16
-rw-r--r--runtime/indirect_reference_table.h12
-rw-r--r--runtime/indirect_reference_table_test.cc1
-rw-r--r--runtime/instruction_set.h50
-rw-r--r--runtime/instrumentation.cc25
-rw-r--r--runtime/instrumentation.h29
-rw-r--r--runtime/intern_table.cc17
-rw-r--r--runtime/intern_table.h3
-rw-r--r--runtime/intern_table_test.cc9
-rw-r--r--runtime/interpreter/interpreter.cc2894
-rw-r--r--runtime/interpreter/interpreter_common.cc348
-rw-r--r--runtime/interpreter/interpreter_common.h677
-rw-r--r--runtime/interpreter/interpreter_goto_table_impl.cc2400
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc2147
-rw-r--r--runtime/jdwp/jdwp_handler.cc4
-rw-r--r--runtime/jni_internal.cc52
-rw-r--r--runtime/jni_internal.h4
-rw-r--r--runtime/jni_internal_test.cc31
-rw-r--r--runtime/lock_word-inl.h55
-rw-r--r--runtime/lock_word.h155
-rw-r--r--runtime/locks.h2
-rw-r--r--runtime/mem_map.cc97
-rw-r--r--runtime/mem_map.h22
-rw-r--r--runtime/mem_map_test.cc70
-rw-r--r--runtime/mirror/array-inl.h65
-rw-r--r--runtime/mirror/array.cc33
-rw-r--r--runtime/mirror/array.h14
-rw-r--r--runtime/mirror/art_method-inl.h7
-rw-r--r--runtime/mirror/art_method.cc10
-rw-r--r--runtime/mirror/art_method.h8
-rw-r--r--runtime/mirror/class-inl.h31
-rw-r--r--runtime/mirror/class.cc80
-rw-r--r--runtime/mirror/class.h68
-rw-r--r--runtime/mirror/object-inl.h26
-rw-r--r--runtime/mirror/object.cc48
-rw-r--r--runtime/mirror/object.h34
-rw-r--r--runtime/mirror/object_test.cc25
-rw-r--r--runtime/mirror/stack_trace_element.cc1
-rw-r--r--runtime/mirror/string.cc8
-rw-r--r--runtime/mirror/string.h3
-rw-r--r--runtime/modifiers.h1
-rw-r--r--runtime/monitor.cc800
-rw-r--r--runtime/monitor.h130
-rw-r--r--runtime/monitor_android.cc2
-rw-r--r--runtime/native/dalvik_system_DexFile.cc172
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc35
-rw-r--r--runtime/native/dalvik_system_VMStack.cc17
-rw-r--r--runtime/native/java_lang_Class.cc11
-rw-r--r--runtime/native/java_lang_DexCache.cc8
-rw-r--r--runtime/native/java_lang_Object.cc22
-rw-r--r--runtime/native/java_lang_Runtime.cc14
-rw-r--r--runtime/native/java_lang_String.cc13
-rw-r--r--runtime/native/java_lang_System.cc36
-rw-r--r--runtime/native/java_lang_Thread.cc13
-rw-r--r--runtime/native/java_lang_VMClassLoader.cc6
-rw-r--r--runtime/native/java_lang_reflect_Array.cc10
-rw-r--r--runtime/native/java_lang_reflect_Constructor.cc3
-rw-r--r--runtime/native/java_lang_reflect_Field.cc54
-rw-r--r--runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc6
-rw-r--r--runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc20
-rw-r--r--runtime/native/scoped_fast_native_object_access.h103
-rw-r--r--runtime/native/sun_misc_Unsafe.cc74
-rw-r--r--runtime/oat.cc59
-rw-r--r--runtime/oat.h24
-rw-r--r--runtime/oat_file.cc263
-rw-r--r--runtime/oat_file.h49
-rw-r--r--runtime/object_utils.h123
-rw-r--r--runtime/reference_table.cc10
-rw-r--r--runtime/reference_table.h6
-rw-r--r--runtime/reflection.cc4
-rw-r--r--runtime/root_visitor.h3
-rw-r--r--runtime/runtime.cc110
-rw-r--r--runtime/runtime.h55
-rw-r--r--runtime/scoped_thread_state_change.h13
-rw-r--r--runtime/signal_catcher.cc1
-rw-r--r--runtime/sirt_ref.h3
-rw-r--r--runtime/stack.cc8
-rw-r--r--runtime/stack.h17
-rw-r--r--runtime/thread-inl.h30
-rw-r--r--runtime/thread.cc306
-rw-r--r--runtime/thread.h61
-rw-r--r--runtime/thread_list.cc218
-rw-r--r--runtime/thread_list.h31
-rw-r--r--runtime/throw_location.cc11
-rw-r--r--runtime/trace.cc2
-rw-r--r--runtime/utf-inl.h61
-rw-r--r--runtime/utf.cc36
-rw-r--r--runtime/utf.h5
-rw-r--r--runtime/utils.cc63
-rw-r--r--runtime/utils.h24
-rw-r--r--runtime/verifier/method_verifier.cc249
-rw-r--r--runtime/verifier/method_verifier.h25
-rw-r--r--runtime/verifier/reg_type.cc16
-rw-r--r--runtime/verifier/reg_type.h9
-rw-r--r--runtime/verifier/reg_type_cache-inl.h21
-rw-r--r--runtime/verifier/reg_type_cache.cc102
-rw-r--r--runtime/verifier/reg_type_cache.h63
-rw-r--r--runtime/verifier/register_line.cc3
-rw-r--r--runtime/verifier/register_line.h32
-rw-r--r--runtime/zip_archive.cc120
-rw-r--r--runtime/zip_archive.h22
-rw-r--r--runtime/zip_archive_test.cc11
-rw-r--r--test/100-reflect2/expected.txt2
-rw-r--r--test/Android.mk3
-rw-r--r--test/GetMethodSignature/GetMethodSignature.java (renamed from test/CreateMethodSignature/CreateMethodSignature.java)4
-rw-r--r--test/InterfaceTest/InterfaceTest.java53
-rwxr-xr-xtest/run-test2
299 files changed, 18182 insertions, 10126 deletions
diff --git a/Android.mk b/Android.mk
index 46a7c1ec3e..3112ab0288 100644
--- a/Android.mk
+++ b/Android.mk
@@ -85,6 +85,7 @@ ifneq ($(art_dont_bother),true)
include $(art_path)/runtime/Android.mk
include $(art_path)/compiler/Android.mk
include $(art_path)/dex2oat/Android.mk
+include $(art_path)/disassembler/Android.mk
include $(art_path)/oatdump/Android.mk
include $(art_path)/dalvikvm/Android.mk
include $(art_path)/jdwpspy/Android.mk
@@ -269,7 +270,7 @@ oat-target-$(1): $$(OUT_OAT_FILE)
$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY)
@mkdir -p $$(dir $$@)
- $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --android-root=$(PRODUCT_OUT)/system
+ $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system
endif
diff --git a/build/Android.common.mk b/build/Android.common.mk
index dd0ba4d1d3..0871884b00 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -105,6 +105,7 @@ ART_HOST_SHLIB_EXTENSION ?= .so
ART_C_INCLUDES := \
external/gtest/include \
external/valgrind/main/include \
+ external/valgrind/main \
external/zlib \
frameworks/compile/mclinker/include
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 5cf15be1c1..b317d92999 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -24,6 +24,10 @@ ifeq ($(ART_USE_PORTABLE_COMPILER),true)
ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
endif
+# add the default instruction set features
+ART_EXECUTABLES_CFLAGS += \
+ -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
+
# $(1): executable ("d" will be appended for debug version)
# $(2): source
# $(3): extra shared libraries
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 4c658a2bb7..0d759ce085 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -17,6 +17,7 @@
LOCAL_PATH := art
TEST_COMMON_SRC_FILES := \
+ compiler/dex/arena_allocator_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
compiler/image_test.cc \
@@ -27,6 +28,7 @@ TEST_COMMON_SRC_FILES := \
compiler/utils/arm/managed_register_arm_test.cc \
compiler/utils/x86/managed_register_x86_test.cc \
runtime/barrier_test.cc \
+ runtime/base/bit_vector_test.cc \
runtime/base/histogram_test.cc \
runtime/base/mutex_test.cc \
runtime/base/timing_logger_test.cc \
@@ -84,6 +86,7 @@ ART_TEST_CFLAGS :=
ifeq ($(ART_USE_PORTABLE_COMPILER),true)
ART_TEST_CFLAGS += -DART_USE_PORTABLE_COMPILER=1
endif
+ART_TEST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
# $(1): target or host
# $(2): file name
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index b680b820b7..5d355a6d86 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -26,7 +26,7 @@ LIBART_COMPILER := $(LIBARTD_COMPILER)
# By default, do not run rerun dex2oat if the tool changes.
# Comment out the | to force dex2oat to rerun on after all changes.
-DEX2OAT_DEPENDENCY := |
+DEX2OAT_DEPENDENCY := #|
DEX2OAT_DEPENDENCY += $(DEX2OAT)
DEX2OAT_DEPENDENCY += $(LIBART_COMPILER)
@@ -57,15 +57,26 @@ TARGET_CORE_OAT_OUT := $(ART_TEST_OUT)/core.oat
HOST_CORE_IMG_OUT := $(HOST_OUT_JAVA_LIBRARIES)/core.art
TARGET_CORE_IMG_OUT := $(ART_TEST_OUT)/core.art
+# DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES is set in ../build/core/dex_preopt.mk based on
+# the TARGET_CPU_VARIANT
+
+TARGET_INSTRUCTION_SET_FEATURES := $(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
+
$(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY)
@echo "host dex2oat: $@ ($?)"
@mkdir -p $(dir $@)
- $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT)
+ $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
+ --dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) \
+ --oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) \
+ --instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT)
$(TARGET_CORE_IMG_OUT): $(TARGET_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY)
@echo "target dex2oat: $@ ($?)"
@mkdir -p $(dir $@)
- $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
+ $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
+ --dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) \
+ --oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \
+ --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
$(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT)
@@ -110,7 +121,10 @@ $(TARGET_BOOT_IMG_OUT): $(TARGET_BOOT_DEX_FILES) $(DEX2OAT_DEPENDENCY)
@echo "target dex2oat: $@ ($?)"
@mkdir -p $(dir $@)
@mkdir -p $(dir $(TARGET_BOOT_OAT_UNSTRIPPED_OUT))
- $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) --instruction-set=$(TARGET_ARCH) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
+ $(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) \
+ --oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) \
+ --oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \
+ --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
$(TARGET_BOOT_OAT_UNSTRIPPED_OUT): $(TARGET_BOOT_IMG_OUT)
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 66ff46163b..fc2f02b59e 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -74,7 +74,6 @@ LIBART_COMPILER_SRC_FILES := \
llvm/md_builder.cc \
llvm/runtime_support_builder.cc \
llvm/runtime_support_builder_arm.cc \
- llvm/runtime_support_builder_thumb2.cc \
llvm/runtime_support_builder_x86.cc \
trampolines/trampoline_compiler.cc \
utils/arm/assembler_arm.cc \
diff --git a/compiler/dex/arena_allocator.cc b/compiler/dex/arena_allocator.cc
index 36393e7387..95e44b3e0d 100644
--- a/compiler/dex/arena_allocator.cc
+++ b/compiler/dex/arena_allocator.cc
@@ -19,12 +19,15 @@
#include "arena_allocator.h"
#include "base/logging.h"
#include "base/mutex.h"
+#include "thread-inl.h"
+#include <memcheck/memcheck.h>
namespace art {
// Memmap is a bit slower than malloc according to my measurements.
static constexpr bool kUseMemMap = false;
static constexpr bool kUseMemSet = true && kUseMemMap;
+static constexpr size_t kValgrindRedZoneBytes = 8;
static const char* alloc_names[ArenaAllocator::kNumAllocKinds] = {
"Misc ",
@@ -47,7 +50,9 @@ Arena::Arena(size_t size)
map_(nullptr),
next_(nullptr) {
if (kUseMemMap) {
- map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE);
+ std::string error_msg;
+ map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE, &error_msg);
+ CHECK(map_ != nullptr) << error_msg;
memory_ = map_->Begin();
size_ = map_->Size();
} else {
@@ -107,6 +112,9 @@ Arena* ArenaPool::AllocArena(size_t size) {
void ArenaPool::FreeArena(Arena* arena) {
Thread* self = Thread::Current();
+ if (UNLIKELY(RUNNING_ON_VALGRIND)) {
+ VALGRIND_MAKE_MEM_UNDEFINED(arena->memory_, arena->bytes_allocated_);
+ }
{
MutexLock lock(self, lock_);
arena->next_ = free_arenas_;
@@ -128,7 +136,8 @@ ArenaAllocator::ArenaAllocator(ArenaPool* pool)
end_(nullptr),
ptr_(nullptr),
arena_head_(nullptr),
- num_allocations_(0) {
+ num_allocations_(0),
+ running_on_valgrind_(RUNNING_ON_VALGRIND) {
memset(&alloc_stats_[0], 0, sizeof(alloc_stats_));
}
@@ -140,6 +149,29 @@ void ArenaAllocator::UpdateBytesAllocated() {
}
}
+void* ArenaAllocator::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
+ size_t rounded_bytes = (bytes + 3 + kValgrindRedZoneBytes) & ~3;
+ if (UNLIKELY(ptr_ + rounded_bytes > end_)) {
+ // Obtain a new block.
+ ObtainNewArenaForAllocation(rounded_bytes);
+ if (UNLIKELY(ptr_ == nullptr)) {
+ return nullptr;
+ }
+ }
+ if (kCountAllocations) {
+ alloc_stats_[kind] += rounded_bytes;
+ ++num_allocations_;
+ }
+ uint8_t* ret = ptr_;
+ ptr_ += rounded_bytes;
+ // Check that the memory is already zeroed out.
+ for (uint8_t* ptr = ret; ptr < ptr_; ++ptr) {
+ CHECK_EQ(*ptr, 0U);
+ }
+ VALGRIND_MAKE_MEM_NOACCESS(ret + bytes, rounded_bytes - bytes);
+ return ret;
+}
+
ArenaAllocator::~ArenaAllocator() {
// Reclaim all the arenas by giving them back to the thread pool.
UpdateBytesAllocated();
diff --git a/compiler/dex/arena_allocator.h b/compiler/dex/arena_allocator.h
index dda52a2ed0..d11d67c795 100644
--- a/compiler/dex/arena_allocator.h
+++ b/compiler/dex/arena_allocator.h
@@ -103,6 +103,9 @@ class ArenaAllocator {
// Returns zeroed memory.
void* Alloc(size_t bytes, ArenaAllocKind kind) ALWAYS_INLINE {
+ if (UNLIKELY(running_on_valgrind_)) {
+ return AllocValgrind(bytes, kind);
+ }
bytes = (bytes + 3) & ~3;
if (UNLIKELY(ptr_ + bytes > end_)) {
// Obtain a new block.
@@ -120,6 +123,7 @@ class ArenaAllocator {
return ret;
}
+ void* AllocValgrind(size_t bytes, ArenaAllocKind kind);
void ObtainNewArenaForAllocation(size_t allocation_size);
size_t BytesAllocated() const;
void DumpMemStats(std::ostream& os) const;
@@ -132,10 +136,9 @@ class ArenaAllocator {
uint8_t* end_;
uint8_t* ptr_;
Arena* arena_head_;
-
- // Statistics.
size_t num_allocations_;
- size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds.
+ size_t alloc_stats_[kNumAllocKinds]; // Bytes used by various allocation kinds.
+ bool running_on_valgrind_;
DISALLOW_COPY_AND_ASSIGN(ArenaAllocator);
}; // ArenaAllocator
diff --git a/compiler/dex/arena_allocator_test.cc b/compiler/dex/arena_allocator_test.cc
new file mode 100644
index 0000000000..63dc6159eb
--- /dev/null
+++ b/compiler/dex/arena_allocator_test.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 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 "arena_allocator.h"
+#include "arena_bit_vector.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(ArenaAllocator, Test) {
+ ArenaPool pool;
+ ArenaAllocator arena(&pool);
+ ArenaBitVector bv(&arena, 10, true);
+ bv.SetBit(5);
+ EXPECT_EQ(1U, bv.GetStorageSize());
+ bv.SetBit(35);
+ EXPECT_EQ(2U, bv.GetStorageSize());
+}
+
+} // namespace art
diff --git a/compiler/dex/arena_bit_vector.cc b/compiler/dex/arena_bit_vector.cc
index 3fa9295276..b567ae8d8a 100644
--- a/compiler/dex/arena_bit_vector.cc
+++ b/compiler/dex/arena_bit_vector.cc
@@ -19,125 +19,29 @@
namespace art {
-// TODO: profile to make sure this is still a win relative to just using shifted masks.
-static uint32_t check_masks[32] = {
- 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
- 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
- 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
- 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
- 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
- 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
- 0x40000000, 0x80000000 };
+class ArenaBitVectorAllocator : public Allocator {
+ public:
+ explicit ArenaBitVectorAllocator(ArenaAllocator* arena) : arena_(arena) {}
+ ~ArenaBitVectorAllocator() {}
-ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits,
- bool expandable, OatBitMapKind kind)
- : arena_(arena),
- expandable_(expandable),
- kind_(kind),
- storage_size_((start_bits + 31) >> 5),
- storage_(static_cast<uint32_t*>(arena_->Alloc(storage_size_ * sizeof(uint32_t),
- ArenaAllocator::kAllocGrowableBitMap))) {
- DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units.
-}
-
-/*
- * Determine whether or not the specified bit is set.
- */
-bool ArenaBitVector::IsBitSet(unsigned int num) {
- DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
-
- unsigned int val = storage_[num >> 5] & check_masks[num & 0x1f];
- return (val != 0);
-}
-
-// Mark all bits bit as "clear".
-void ArenaBitVector::ClearAllBits() {
- memset(storage_, 0, storage_size_ * sizeof(uint32_t));
-}
-
-// Mark the specified bit as "set".
-/*
- * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're
- * not using it badly or change resize mechanism.
- */
-void ArenaBitVector::SetBit(unsigned int num) {
- if (num >= storage_size_ * sizeof(uint32_t) * 8) {
- DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
-
- /* Round up to word boundaries for "num+1" bits */
- unsigned int new_size = (num + 1 + 31) >> 5;
- DCHECK_GT(new_size, storage_size_);
- uint32_t *new_storage =
- static_cast<uint32_t*>(arena_->Alloc(new_size * sizeof(uint32_t),
- ArenaAllocator::kAllocGrowableBitMap));
- memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t));
- // Zero out the new storage words.
- memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t));
- // TOTO: collect stats on space wasted because of resize.
- storage_ = new_storage;
- storage_size_ = new_size;
+ virtual void* Alloc(size_t size) {
+ return arena_->Alloc(size, ArenaAllocator::kAllocGrowableBitMap);
}
- storage_[num >> 5] |= check_masks[num & 0x1f];
-}
-
-// Mark the specified bit as "unset".
-void ArenaBitVector::ClearBit(unsigned int num) {
- DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
- storage_[num >> 5] &= ~check_masks[num & 0x1f];
-}
-
-// Copy a whole vector to the other. Sizes must match.
-void ArenaBitVector::Copy(ArenaBitVector* src) {
- DCHECK_EQ(storage_size_, src->GetStorageSize());
- memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_);
-}
+ virtual void Free(void*) {} // Nop.
-// Intersect with another bit vector. Sizes and expandability must be the same.
-void ArenaBitVector::Intersect(const ArenaBitVector* src) {
- DCHECK_EQ(storage_size_, src->GetStorageSize());
- DCHECK_EQ(expandable_, src->IsExpandable());
- for (unsigned int idx = 0; idx < storage_size_; idx++) {
- storage_[idx] &= src->GetRawStorageWord(idx);
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->Alloc(sizeof(ArenaBitVectorAllocator), ArenaAllocator::kAllocGrowableBitMap);
}
-}
+ static void operator delete(void* p) {} // Nop.
-/*
- * Union with another bit vector. Sizes and expandability must be the same.
- */
-void ArenaBitVector::Union(const ArenaBitVector* src) {
- DCHECK_EQ(storage_size_, src->GetStorageSize());
- DCHECK_EQ(expandable_, src->IsExpandable());
- for (unsigned int idx = 0; idx < storage_size_; idx++) {
- storage_[idx] |= src->GetRawStorageWord(idx);
- }
-}
-
-// Count the number of bits that are set.
-int ArenaBitVector::NumSetBits() {
- unsigned int count = 0;
-
- for (unsigned int word = 0; word < storage_size_; word++) {
- count += __builtin_popcount(storage_[word]);
- }
- return count;
-}
+ private:
+ ArenaAllocator* arena_;
+ DISALLOW_COPY_AND_ASSIGN(ArenaBitVectorAllocator);
+};
-/*
- * Mark specified number of bits as "set". Cannot set all bits like ClearAll
- * since there might be unused bits - setting those to one will confuse the
- * iterator.
- */
-void ArenaBitVector::SetInitialBits(unsigned int num_bits) {
- DCHECK_LE(((num_bits + 31) >> 5), storage_size_);
- unsigned int idx;
- for (idx = 0; idx < (num_bits >> 5); idx++) {
- storage_[idx] = -1;
- }
- unsigned int rem_num_bits = num_bits & 0x1f;
- if (rem_num_bits) {
- storage_[idx] = (1 << rem_num_bits) - 1;
- }
-}
+ArenaBitVector::ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits,
+ bool expandable, OatBitMapKind kind)
+ : BitVector(start_bits, expandable, new (arena) ArenaBitVectorAllocator(arena)), kind_(kind) {}
} // namespace art
diff --git a/compiler/dex/arena_bit_vector.h b/compiler/dex/arena_bit_vector.h
index 8bcd628dc0..4b2193a3f1 100644
--- a/compiler/dex/arena_bit_vector.h
+++ b/compiler/dex/arena_bit_vector.h
@@ -17,107 +17,28 @@
#ifndef ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_
#define ART_COMPILER_DEX_ARENA_BIT_VECTOR_H_
-#include <stdint.h>
-#include <stddef.h>
-#include "compiler_enums.h"
#include "arena_allocator.h"
+#include "base/bit_vector.h"
+#include "compiler_enums.h"
namespace art {
/*
- * Expanding bitmap, used for tracking resources. Bits are numbered starting
- * from zero. All operations on a BitVector are unsynchronized.
+ * A BitVector implementation that uses Arena allocation.
*/
-class ArenaBitVector {
+class ArenaBitVector : public BitVector {
public:
- class Iterator {
- public:
- explicit Iterator(ArenaBitVector* bit_vector)
- : p_bits_(bit_vector),
- bit_storage_(bit_vector->GetRawStorage()),
- bit_index_(0),
- bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {}
-
- // Return the position of the next set bit. -1 means end-of-element reached.
- int Next() {
- // Did anything obviously change since we started?
- DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8);
- DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage());
-
- if (bit_index_ >= bit_size_) return -1;
-
- uint32_t word_index = bit_index_ / 32;
- uint32_t word = bit_storage_[word_index];
- // Mask out any bits in the first word we've already considered.
- word >>= bit_index_ & 0x1f;
- if (word == 0) {
- bit_index_ &= ~0x1f;
- do {
- word_index++;
- if ((word_index * 32) >= bit_size_) {
- bit_index_ = bit_size_;
- return -1;
- }
- word = bit_storage_[word_index];
- bit_index_ += 32;
- } while (word == 0);
- }
- bit_index_ += CTZ(word) + 1;
- return bit_index_ - 1;
- }
-
- static void* operator new(size_t size, ArenaAllocator* arena) {
- return arena->Alloc(sizeof(ArenaBitVector::Iterator),
- ArenaAllocator::kAllocGrowableBitMap);
- };
- static void operator delete(void* p) {} // Nop.
-
- private:
- ArenaBitVector* const p_bits_;
- uint32_t* const bit_storage_;
- uint32_t bit_index_; // Current index (size in bits).
- const uint32_t bit_size_; // Size of vector in bits.
- };
-
- ArenaBitVector(ArenaAllocator* arena, unsigned int start_bits, bool expandable,
+ ArenaBitVector(ArenaAllocator* arena, uint32_t start_bits, bool expandable,
OatBitMapKind kind = kBitMapMisc);
~ArenaBitVector() {}
- static void* operator new(size_t size, ArenaAllocator* arena) {
- return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap);
- }
- static void operator delete(void* p) {} // Nop.
-
- void SetBit(unsigned int num);
- void ClearBit(unsigned int num);
- void MarkAllBits(bool set);
- void DebugBitVector(char* msg, int length);
- bool IsBitSet(unsigned int num);
- void ClearAllBits();
- void SetInitialBits(unsigned int num_bits);
- void Copy(ArenaBitVector* src);
- void Intersect(const ArenaBitVector* src2);
- void Union(const ArenaBitVector* src);
- // Are we equal to another bit vector? Note: expandability attributes must also match.
- bool Equal(const ArenaBitVector* src) {
- return (storage_size_ == src->GetStorageSize()) &&
- (expandable_ == src->IsExpandable()) &&
- (memcmp(storage_, src->GetRawStorage(), storage_size_ * 4) == 0);
- }
- int NumSetBits();
-
- uint32_t GetStorageSize() const { return storage_size_; }
- bool IsExpandable() const { return expandable_; }
- uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; }
- uint32_t* GetRawStorage() { return storage_; }
- const uint32_t* GetRawStorage() const { return storage_; }
+ static void* operator new(size_t size, ArenaAllocator* arena) {
+ return arena->Alloc(sizeof(ArenaBitVector), ArenaAllocator::kAllocGrowableBitMap);
+ }
+ static void operator delete(void* p) {} // Nop.
private:
- ArenaAllocator* const arena_;
- const bool expandable_; // expand bitmap if we run out?
- const OatBitMapKind kind_; // for memory use tuning.
- uint32_t storage_size_; // current size, in 32-bit words.
- uint32_t* storage_;
+ const OatBitMapKind kind_; // for memory use tuning. TODO: currently unused.
};
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index 97a682f2aa..56facfd889 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -44,6 +44,8 @@ enum SpecialTargetRegister {
kRet0,
kRet1,
kInvokeTgt,
+ kHiddenArg,
+ kHiddenFpArg,
kCount
};
@@ -55,6 +57,7 @@ enum RegLocationType {
};
enum BBType {
+ kNullBlock,
kEntryBlock,
kDalvikByteCode,
kExitBlock,
@@ -180,6 +183,8 @@ enum OpKind {
kOpBic,
kOpCmn,
kOpTst,
+ kOpRev,
+ kOpRevsh,
kOpBkpt,
kOpBlx,
kOpPush,
@@ -412,6 +417,27 @@ enum OatBitMapKind {
std::ostream& operator<<(std::ostream& os, const OatBitMapKind& kind);
+// LIR fixup kinds for Arm
+enum FixupKind {
+ kFixupNone,
+ kFixupLabel, // For labels we just adjust the offset.
+ kFixupLoad, // Mostly for imediates.
+ kFixupVLoad, // FP load which *may* be pc-relative.
+ kFixupCBxZ, // Cbz, Cbnz.
+ kFixupPushPop, // Not really pc relative, but changes size based on args.
+ kFixupCondBranch, // Conditional branch
+ kFixupT1Branch, // Thumb1 Unconditional branch
+ kFixupT2Branch, // Thumb2 Unconditional branch
+ kFixupBlx1, // Blx1 (start of Blx1/Blx2 pair).
+ kFixupBl1, // Bl1 (start of Bl1/Bl2 pair).
+ kFixupAdr, // Adr.
+ kFixupMovImmLST, // kThumb2MovImm16LST.
+ kFixupMovImmHST, // kThumb2MovImm16HST.
+ kFixupAlign4, // Align to 4-byte boundary.
+};
+
+std::ostream& operator<<(std::ostream& os, const FixupKind& kind);
+
} // namespace art
#endif // ART_COMPILER_DEX_COMPILER_ENUMS_H_
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 6607562b13..fd46975b9a 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -29,6 +29,7 @@
#include "llvm/intrinsic_helper.h"
#include "llvm/ir_builder.h"
#include "safe_map.h"
+#include "base/timing_logger.h"
namespace art {
@@ -68,7 +69,14 @@ struct CompilationUnit {
compiler_flip_match(false),
arena(pool),
mir_graph(NULL),
- cg(NULL) {}
+ cg(NULL),
+ timings("QuickCompiler", true, false) {
+ }
+
+ void StartTimingSplit(const char* label);
+ void NewTimingSplit(const char* label);
+ void EndTiming();
+
/*
* Fields needed/generated by common frontend and generally used throughout
* the compiler.
@@ -89,15 +97,18 @@ struct CompilationUnit {
CompilerBackend compiler_backend;
InstructionSet instruction_set;
+ const InstructionSetFeatures& GetInstructionSetFeatures() {
+ return compiler_driver->GetInstructionSetFeatures();
+ }
// TODO: much of this info available elsewhere. Go to the original source?
- int num_dalvik_registers; // method->registers_size.
+ uint16_t num_dalvik_registers; // method->registers_size.
const uint16_t* insns;
- int num_ins;
- int num_outs;
- int num_regs; // Unlike num_dalvik_registers, does not include ins.
+ uint16_t num_ins;
+ uint16_t num_outs;
+ uint16_t num_regs; // Unlike num_dalvik_registers, does not include ins.
// TODO: may want to move this to MIRGraph.
- int num_compiler_temps;
+ uint16_t num_compiler_temps;
// If non-empty, apply optimizer/debug flags only to matching methods.
std::string compiler_method_match;
@@ -109,6 +120,7 @@ struct CompilationUnit {
UniquePtr<MIRGraph> mir_graph; // MIR container.
UniquePtr<Backend> cg; // Target-specific codegen.
+ base::TimingLogger timings;
};
} // namespace art
diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h
index 06cc505a9a..74f36ddd81 100644
--- a/compiler/dex/dataflow_iterator-inl.h
+++ b/compiler/dex/dataflow_iterator-inl.h
@@ -21,42 +21,63 @@
namespace art {
-inline BasicBlock* DataflowIterator::NextBody(bool had_change) {
+// Single forward pass over the nodes.
+inline BasicBlock* DataflowIterator::ForwardSingleNext() {
+ BasicBlock* res = NULL;
+ if (idx_ < end_idx_) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_++);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ return res;
+}
+
+// Repeat full forward passes over all nodes until no change occurs during a complete pass.
+inline BasicBlock* DataflowIterator::ForwardRepeatNext(bool had_change) {
changed_ |= had_change;
BasicBlock* res = NULL;
- if (reverse_) {
- if (is_iterative_ && changed_ && (idx_ < 0)) {
- idx_ = start_idx_;
- changed_ = false;
- }
- if (idx_ >= 0) {
- int bb_id = block_id_list_->Get(idx_--);
- res = mir_graph_->GetBasicBlock(bb_id);
- }
- } else {
- if (is_iterative_ && changed_ && (idx_ >= end_idx_)) {
- idx_ = start_idx_;
- changed_ = false;
- }
- if (idx_ < end_idx_) {
- int bb_id = block_id_list_->Get(idx_++);
- res = mir_graph_->GetBasicBlock(bb_id);
- }
+ if ((idx_ >= end_idx_) && changed_) {
+ idx_ = start_idx_;
+ changed_ = false;
+ }
+ if (idx_ < end_idx_) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_++);
+ res = mir_graph_->GetBasicBlock(bb_id);
}
return res;
}
-// AllNodes uses the existing GrowableArray iterator, so use different NextBody().
-inline BasicBlock* AllNodesIterator::NextBody(bool had_change) {
+// Single reverse pass over the nodes.
+inline BasicBlock* DataflowIterator::ReverseSingleNext() {
+ BasicBlock* res = NULL;
+ if (idx_ >= 0) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_--);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ return res;
+}
+
+// Repeat full backwards passes over all nodes until no change occurs during a complete pass.
+inline BasicBlock* DataflowIterator::ReverseRepeatNext(bool had_change) {
changed_ |= had_change;
BasicBlock* res = NULL;
+ if ((idx_ < 0) && changed_) {
+ idx_ = start_idx_;
+ changed_ = false;
+ }
+ if (idx_ >= 0) {
+ BasicBlockId bb_id = block_id_list_->Get(idx_--);
+ res = mir_graph_->GetBasicBlock(bb_id);
+ }
+ return res;
+}
+
+// AllNodes uses the existing GrowableArray iterator, and should be considered unordered.
+inline BasicBlock* AllNodesIterator::Next() {
+ BasicBlock* res = NULL;
bool keep_looking = true;
while (keep_looking) {
res = all_nodes_iterator_->Next();
- if (is_iterative_ && changed_ && (res == NULL)) {
- all_nodes_iterator_->Reset();
- changed_ = false;
- } else if ((res == NULL) || (!res->hidden)) {
+ if ((res == NULL) || (!res->hidden)) {
keep_looking = false;
}
}
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index da44ffd99c..26e36653be 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -27,124 +27,130 @@ namespace art {
* interesting orders. Note that for efficiency, the visit orders have been pre-computed.
* The order itself will not change during the iteration. However, for some uses,
* auxiliary data associated with the basic blocks may be changed during the iteration,
- * necessitating another pass over the list.
- *
- * To support this usage, we have is_iterative_. If false, the iteration is a one-shot
- * pass through the pre-computed list using Next(). If true, the caller must tell the
- * iterator whether a change has been made that necessitates another pass. Use
- * Next(had_change) for this. The general idea is that the iterative_ use case means
- * that the iterator will keep repeating the full basic block list until a complete pass
- * is made through it with no changes. Note that calling Next(true) does not affect
- * the iteration order or short-curcuit the current pass - it simply tells the iterator
- * that once it has finished walking through the block list it should reset and do another
- * full pass through the list.
+ * necessitating another pass over the list. If this behavior is required, use the
+ * "Repeating" variant. For the repeating variant, the caller must tell the iterator
+ * whether a change has been made that necessitates another pass. Note that calling Next(true)
+ * does not affect the iteration order or short-circuit the current pass - it simply tells
+ * the iterator that once it has finished walking through the block list it should reset and
+ * do another full pass through the list.
*/
class DataflowIterator {
public:
virtual ~DataflowIterator() {}
- // Return the next BasicBlock* to visit.
- BasicBlock* Next() {
- DCHECK(!is_iterative_);
- return NextBody(false);
- }
-
- /*
- * Return the next BasicBlock* to visit, and tell the iterator whether any change
- * has occurred that requires another full pass over the block list.
- */
- BasicBlock* Next(bool had_change) {
- DCHECK(is_iterative_);
- return NextBody(had_change);
- }
-
protected:
- DataflowIterator(MIRGraph* mir_graph, bool is_iterative, int start_idx, int end_idx,
- bool reverse)
+ DataflowIterator(MIRGraph* mir_graph, int32_t start_idx, int32_t end_idx)
: mir_graph_(mir_graph),
- is_iterative_(is_iterative),
start_idx_(start_idx),
end_idx_(end_idx),
- reverse_(reverse),
block_id_list_(NULL),
idx_(0),
changed_(false) {}
- virtual BasicBlock* NextBody(bool had_change) ALWAYS_INLINE;
+ virtual BasicBlock* ForwardSingleNext() ALWAYS_INLINE;
+ virtual BasicBlock* ReverseSingleNext() ALWAYS_INLINE;
+ virtual BasicBlock* ForwardRepeatNext(bool had_change) ALWAYS_INLINE;
+ virtual BasicBlock* ReverseRepeatNext(bool had_change) ALWAYS_INLINE;
MIRGraph* const mir_graph_;
- const bool is_iterative_;
- const int start_idx_;
- const int end_idx_;
- const bool reverse_;
- GrowableArray<int>* block_id_list_;
- int idx_;
+ const int32_t start_idx_;
+ const int32_t end_idx_;
+ GrowableArray<BasicBlockId>* block_id_list_;
+ int32_t idx_;
bool changed_;
}; // DataflowIterator
- class ReachableNodesIterator : public DataflowIterator {
+ class PreOrderDfsIterator : public DataflowIterator {
public:
- ReachableNodesIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit PreOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsOrder();
}
+
+ BasicBlock* Next() {
+ return ForwardSingleNext();
+ }
};
- class PreOrderDfsIterator : public DataflowIterator {
+ class RepeatingPreOrderDfsIterator : public DataflowIterator {
public:
- PreOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit RepeatingPreOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsOrder();
}
+
+ BasicBlock* Next(bool had_change) {
+ return ForwardRepeatNext(had_change);
+ }
};
- class PostOrderDfsIterator : public DataflowIterator {
+ class RepeatingPostOrderDfsIterator : public DataflowIterator {
public:
- PostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit RepeatingPostOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsPostOrder();
}
+
+ BasicBlock* Next(bool had_change) {
+ return ForwardRepeatNext(had_change);
+ }
};
class ReversePostOrderDfsIterator : public DataflowIterator {
public:
- ReversePostOrderDfsIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative,
- mir_graph->GetNumReachableBlocks() -1, 0, true) {
+ explicit ReversePostOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetDfsPostOrder();
+ }
+
+ BasicBlock* Next() {
+ return ReverseSingleNext();
+ }
+ };
+
+ class RepeatingReversePostOrderDfsIterator : public DataflowIterator {
+ public:
+ explicit RepeatingReversePostOrderDfsIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, mir_graph->GetNumReachableBlocks() -1, 0) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDfsPostOrder();
}
+
+ BasicBlock* Next(bool had_change) {
+ return ReverseRepeatNext(had_change);
+ }
};
class PostOrderDOMIterator : public DataflowIterator {
public:
- PostOrderDOMIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0,
- mir_graph->GetNumReachableBlocks(), false) {
+ explicit PostOrderDOMIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetNumReachableBlocks()) {
idx_ = start_idx_;
block_id_list_ = mir_graph->GetDomPostOrder();
}
+
+ BasicBlock* Next() {
+ return ForwardSingleNext();
+ }
};
class AllNodesIterator : public DataflowIterator {
public:
- AllNodesIterator(MIRGraph* mir_graph, bool is_iterative)
- : DataflowIterator(mir_graph, is_iterative, 0, 0, false) {
- all_nodes_iterator_ =
- new (mir_graph->GetArena()) GrowableArray<BasicBlock*>::Iterator(mir_graph->GetBlockList());
+ explicit AllNodesIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, 0) {
+ all_nodes_iterator_ = new
+ (mir_graph->GetArena()) GrowableArray<BasicBlock*>::Iterator(mir_graph->GetBlockList());
}
void Reset() {
all_nodes_iterator_->Reset();
}
- BasicBlock* NextBody(bool had_change) ALWAYS_INLINE;
+ BasicBlock* Next() ALWAYS_INLINE;
private:
GrowableArray<BasicBlock*>::Iterator* all_nodes_iterator_;
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 63d8aa04f8..abafbc5830 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -24,6 +24,7 @@
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
+#include "thread-inl.h"
namespace art {
namespace optimizer {
@@ -216,8 +217,8 @@ void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
uint32_t field_idx = inst->VRegC_22c();
int field_offset;
bool is_volatile;
- bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, field_offset,
- is_volatile, is_put);
+ bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put,
+ &field_offset, &is_volatile);
if (fast_path && !is_volatile && IsUint(16, field_offset)) {
VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
<< " to " << Instruction::Name(new_opcode)
@@ -246,11 +247,13 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst,
int vtable_idx;
uintptr_t direct_code;
uintptr_t direct_method;
- bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc, invoke_type,
- target_method, vtable_idx,
- direct_code, direct_method,
- false);
// TODO: support devirtualization.
+ const bool kEnableDevirtualization = false;
+ bool fast_path = driver_.ComputeInvokeInfo(&unit_, dex_pc,
+ false, kEnableDevirtualization,
+ &invoke_type,
+ &target_method, &vtable_idx,
+ &direct_code, &direct_method);
if (fast_path && original_invoke_type == invoke_type) {
if (vtable_idx >= 0 && IsUint(16, vtable_idx)) {
VLOG(compiler) << "Quickening " << Instruction::Name(inst->Opcode())
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index fefcab9e87..2f8521f788 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -24,6 +24,7 @@
#include "runtime.h"
#include "backend.h"
#include "base/logging.h"
+#include "base/timing_logger.h"
#if defined(ART_USE_PORTABLE_COMPILER)
#include "dex/portable/mir_to_gbc.h"
@@ -104,8 +105,30 @@ static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes
// (1 << kDebugVerifyBitcode) |
// (1 << kDebugShowSummaryMemoryUsage) |
// (1 << kDebugShowFilterStats) |
+ // (1 << kDebugTimings) |
0;
+// TODO: Add a cumulative version of logging, and combine with dex2oat --dump-timing
+void CompilationUnit::StartTimingSplit(const char* label) {
+ if (enable_debug & (1 << kDebugTimings)) {
+ timings.StartSplit(label);
+ }
+}
+
+void CompilationUnit::NewTimingSplit(const char* label) {
+ if (enable_debug & (1 << kDebugTimings)) {
+ timings.NewSplit(label);
+ }
+}
+
+void CompilationUnit::EndTiming() {
+ if (enable_debug & (1 << kDebugTimings)) {
+ timings.EndSplit();
+ LOG(INFO) << "TIMINGS " << PrettyMethod(method_idx, *dex_file);
+ LOG(INFO) << Dumpable<base::TimingLogger>(timings);
+ }
+}
+
static CompiledMethod* CompileMethod(CompilerDriver& compiler,
const CompilerBackend compiler_backend,
const DexFile::CodeItem* code_item,
@@ -117,6 +140,11 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
#endif
) {
VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
+ if (code_item->insns_size_in_code_units_ >= 0x10000) {
+ LOG(INFO) << "Method size exceeds compiler limits: " << code_item->insns_size_in_code_units_
+ << " in " << PrettyMethod(method_idx, dex_file);
+ return NULL;
+ }
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
CompilationUnit cu(&compiler.GetArenaPool());
@@ -151,7 +179,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
*/
if (compiler_backend == kPortable) {
- // Fused long branches not currently usseful in bitcode.
+ // Fused long branches not currently useful in bitcode.
cu.disable_opt |= (1 << kBranchFusing);
}
@@ -170,6 +198,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
(1 << kPromoteCompilerTemps));
}
+ cu.StartTimingSplit("BuildMIRGraph");
cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
/* Gathering opcode stats? */
@@ -187,22 +216,28 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
}
#endif
+ cu.NewTimingSplit("MIROpt:CodeLayout");
+
/* Do a code layout pass */
cu.mir_graph->CodeLayout();
/* Perform SSA transformation for the whole method */
+ cu.NewTimingSplit("MIROpt:SSATransform");
cu.mir_graph->SSATransformation();
/* Do constant propagation */
+ cu.NewTimingSplit("MIROpt:ConstantProp");
cu.mir_graph->PropagateConstants();
/* Count uses */
cu.mir_graph->MethodUseCount();
/* Perform null check elimination */
+ cu.NewTimingSplit("MIROpt:NullCheckElimination");
cu.mir_graph->NullCheckElimination();
/* Combine basic blocks where possible */
+ cu.NewTimingSplit("MIROpt:BBOpt");
cu.mir_graph->BasicBlockCombine();
/* Do some basic block optimizations */
@@ -245,6 +280,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
cu.cg->Materialize();
+ cu.NewTimingSplit("Cleanup");
result = cu.cg->GetCompiledMethod();
if (result) {
@@ -265,6 +301,7 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
<< " " << PrettyMethod(method_idx, dex_file);
}
+ cu.EndTiming();
return result;
}
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
index 6c33d109e3..43f68554b5 100644
--- a/compiler/dex/frontend.h
+++ b/compiler/dex/frontend.h
@@ -78,6 +78,7 @@ enum debugControlVector {
kDebugVerifyBitcode,
kDebugShowSummaryMemoryUsage,
kDebugShowFilterStats,
+ kDebugTimings
};
class LLVMInfo {
diff --git a/compiler/dex/growable_array.h b/compiler/dex/growable_array.h
index 8e2abfbaf1..639120a2ba 100644
--- a/compiler/dex/growable_array.h
+++ b/compiler/dex/growable_array.h
@@ -131,6 +131,11 @@ class GrowableArray {
elem_list_[index]++;
}
+ /*
+ * Remove an existing element from list. If there are more than one copy
+ * of the element, only the first one encountered will be deleted.
+ */
+ // TODO: consider renaming this.
void Delete(T element) {
bool found = false;
for (size_t i = 0; i < num_used_ - 1; i++) {
@@ -150,6 +155,11 @@ class GrowableArray {
size_t Size() const { return num_used_; }
+ void SetSize(size_t new_size) {
+ Resize(new_size);
+ num_used_ = new_size;
+ }
+
T* GetRawStorage() const { return elem_list_; }
static void* operator new(size_t size, ArenaAllocator* arena) {
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index d7a4136a01..89af06e085 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -864,7 +864,7 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) {
if (ending_bb->last_mir_insn != NULL) {
uint32_t ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
while ((ending_flags & AN_BRANCH) == 0) {
- ending_bb = ending_bb->fall_through;
+ ending_bb = GetBasicBlock(ending_bb->fall_through);
ending_flags = analysis_attributes_[ending_bb->last_mir_insn->dalvikInsn.opcode];
}
}
@@ -876,13 +876,14 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) {
*/
int loop_scale_factor = 1;
// Simple for and while loops
- if ((ending_bb->taken != NULL) && (ending_bb->fall_through == NULL)) {
- if ((ending_bb->taken->taken == bb) || (ending_bb->taken->fall_through == bb)) {
+ if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->fall_through == NullBasicBlockId)) {
+ if ((GetBasicBlock(ending_bb->taken)->taken == bb->id) ||
+ (GetBasicBlock(ending_bb->taken)->fall_through == bb->id)) {
loop_scale_factor = 25;
}
}
// Simple do-while loop
- if ((ending_bb->taken != NULL) && (ending_bb->taken == bb)) {
+ if ((ending_bb->taken != NullBasicBlockId) && (ending_bb->taken == bb->id)) {
loop_scale_factor = 25;
}
@@ -922,7 +923,7 @@ void MIRGraph::AnalyzeBlock(BasicBlock* bb, MethodStats* stats) {
if (tbb == ending_bb) {
done = true;
} else {
- tbb = tbb->fall_through;
+ tbb = GetBasicBlock(tbb->fall_through);
}
}
if (has_math && computational_block && (loop_scale_factor > 1)) {
@@ -1032,6 +1033,14 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
*/
if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
skip_compilation = true;
+ // If we're got a huge number of basic blocks, don't bother with further analysis.
+ if (static_cast<size_t>(num_blocks_) > (Runtime::Current()->GetHugeMethodThreshold() / 2)) {
+ return true;
+ }
+ } else if (GetNumDalvikInsns() > Runtime::Current()->GetLargeMethodThreshold() &&
+ /* If it's large and contains no branches, it's likely to be machine generated initialization */
+ (GetBranchCount() == 0)) {
+ return true;
} else if (compiler_filter == Runtime::kSpeed) {
// If not huge, compile.
return false;
@@ -1061,7 +1070,7 @@ bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
memset(&stats, 0, sizeof(stats));
ClearAllVisitedFlags();
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
AnalyzeBlock(bb, &stats);
}
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index 3a73717a7b..11e19dc43f 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -1221,10 +1221,10 @@ bool MIRGraph::InvokeUsesMethodStar(MIR* mir) {
uint32_t current_offset = static_cast<uint32_t>(current_offset_);
bool fast_path =
cu_->compiler_driver->ComputeInvokeInfo(&m_unit, current_offset,
- type, target_method,
- vtable_idx,
- direct_code, direct_method,
- false) &&
+ false, true,
+ &type, &target_method,
+ &vtable_idx,
+ &direct_code, &direct_method) &&
!(cu_->enable_debug & (1 << kDebugSlowInvokePath));
return (((type == kDirect) || (type == kStatic)) &&
fast_path && ((direct_code == 0) || (direct_method == 0)));
@@ -1243,7 +1243,8 @@ bool MIRGraph::CountUses(struct BasicBlock* bb) {
if (mir->ssa_rep == NULL) {
continue;
}
- uint32_t weight = std::min(16U, static_cast<uint32_t>(bb->nesting_depth));
+ // Each level of nesting adds *16 to count, up to 3 levels deep.
+ uint32_t weight = std::min(3U, static_cast<uint32_t>(bb->nesting_depth) * 4);
for (int i = 0; i < mir->ssa_rep->num_uses; i++) {
int s_reg = mir->ssa_rep->uses[i];
raw_use_counts_.Increment(s_reg);
@@ -1287,7 +1288,7 @@ void MIRGraph::MethodUseCount() {
if (cu_->disable_opt & (1 << kPromoteRegs)) {
return;
}
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CountUses(bb);
}
@@ -1295,23 +1296,23 @@ void MIRGraph::MethodUseCount() {
/* Verify if all the successor is connected with all the claimed predecessors */
bool MIRGraph::VerifyPredInfo(BasicBlock* bb) {
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
while (true) {
- BasicBlock *pred_bb = iter.Next();
+ BasicBlock *pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) break;
bool found = false;
- if (pred_bb->taken == bb) {
+ if (pred_bb->taken == bb->id) {
found = true;
- } else if (pred_bb->fall_through == bb) {
+ } else if (pred_bb->fall_through == bb->id) {
found = true;
- } else if (pred_bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(pred_bb->successor_block_list.blocks);
+ } else if (pred_bb->successor_block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(pred_bb->successor_blocks);
while (true) {
SuccessorBlockInfo *successor_block_info = iterator.Next();
if (successor_block_info == NULL) break;
- BasicBlock *succ_bb = successor_block_info->block;
- if (succ_bb == bb) {
+ BasicBlockId succ_bb = successor_block_info->block;
+ if (succ_bb == bb->id) {
found = true;
break;
}
@@ -1331,7 +1332,7 @@ bool MIRGraph::VerifyPredInfo(BasicBlock* bb) {
void MIRGraph::VerifyDataflow() {
/* Verify if all blocks are connected as claimed */
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
VerifyPredInfo(bb);
}
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index a12bf39e64..cf758fc5da 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -96,9 +96,9 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
try_block_addr_(NULL),
entry_block_(NULL),
exit_block_(NULL),
- cur_block_(NULL),
num_blocks_(0),
current_code_item_(NULL),
+ dex_pc_to_block_map_(arena, 0, kGrowableArrayMisc),
current_method_(kInvalidEntry),
current_offset_(kInvalidEntry),
def_count_(0),
@@ -108,7 +108,9 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
attributes_(METHOD_IS_LEAF), // Start with leaf assumption, change on encountering invoke.
checkstats_(NULL),
special_case_(kNoHandler),
- arena_(arena) {
+ arena_(arena),
+ backward_branches_(0),
+ forward_branches_(0) {
try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */);
}
@@ -128,11 +130,14 @@ int MIRGraph::ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_in
/* Split an existing block from the specified code offset into two */
-BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset,
+BasicBlock* MIRGraph::SplitBlock(DexOffset code_offset,
BasicBlock* orig_block, BasicBlock** immed_pred_block_p) {
+ DCHECK_GT(code_offset, orig_block->start_offset);
MIR* insn = orig_block->first_mir_insn;
+ MIR* prev = NULL;
while (insn) {
if (insn->offset == code_offset) break;
+ prev = insn;
insn = insn->next;
}
if (insn == NULL) {
@@ -150,43 +155,46 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset,
orig_block->terminated_by_return = false;
/* Add it to the quick lookup cache */
- block_map_.Put(bottom_block->start_offset, bottom_block);
+ dex_pc_to_block_map_.Put(bottom_block->start_offset, bottom_block->id);
/* Handle the taken path */
bottom_block->taken = orig_block->taken;
- if (bottom_block->taken) {
- orig_block->taken = NULL;
- bottom_block->taken->predecessors->Delete(orig_block);
- bottom_block->taken->predecessors->Insert(bottom_block);
+ if (bottom_block->taken != NullBasicBlockId) {
+ orig_block->taken = NullBasicBlockId;
+ BasicBlock* bb_taken = GetBasicBlock(bottom_block->taken);
+ bb_taken->predecessors->Delete(orig_block->id);
+ bb_taken->predecessors->Insert(bottom_block->id);
}
/* Handle the fallthrough path */
bottom_block->fall_through = orig_block->fall_through;
- orig_block->fall_through = bottom_block;
- bottom_block->predecessors->Insert(orig_block);
- if (bottom_block->fall_through) {
- bottom_block->fall_through->predecessors->Delete(orig_block);
- bottom_block->fall_through->predecessors->Insert(bottom_block);
+ orig_block->fall_through = bottom_block->id;
+ bottom_block->predecessors->Insert(orig_block->id);
+ if (bottom_block->fall_through != NullBasicBlockId) {
+ BasicBlock* bb_fall_through = GetBasicBlock(bottom_block->fall_through);
+ bb_fall_through->predecessors->Delete(orig_block->id);
+ bb_fall_through->predecessors->Insert(bottom_block->id);
}
/* Handle the successor list */
- if (orig_block->successor_block_list.block_list_type != kNotUsed) {
- bottom_block->successor_block_list = orig_block->successor_block_list;
- orig_block->successor_block_list.block_list_type = kNotUsed;
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_block_list.blocks);
+ if (orig_block->successor_block_list_type != kNotUsed) {
+ bottom_block->successor_block_list_type = orig_block->successor_block_list_type;
+ bottom_block->successor_blocks = orig_block->successor_blocks;
+ orig_block->successor_block_list_type = kNotUsed;
+ orig_block->successor_blocks = NULL;
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bottom_block->successor_blocks);
while (true) {
SuccessorBlockInfo *successor_block_info = iterator.Next();
if (successor_block_info == NULL) break;
- BasicBlock *bb = successor_block_info->block;
- bb->predecessors->Delete(orig_block);
- bb->predecessors->Insert(bottom_block);
+ BasicBlock *bb = GetBasicBlock(successor_block_info->block);
+ bb->predecessors->Delete(orig_block->id);
+ bb->predecessors->Insert(bottom_block->id);
}
}
- orig_block->last_mir_insn = insn->prev;
+ orig_block->last_mir_insn = prev;
+ prev->next = NULL;
- insn->prev->next = NULL;
- insn->prev = NULL;
/*
* Update the immediate predecessor block pointer so that outgoing edges
* can be applied to the proper block.
@@ -195,6 +203,23 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset,
DCHECK_EQ(*immed_pred_block_p, orig_block);
*immed_pred_block_p = bottom_block;
}
+
+ // Associate dex instructions in the bottom block with the new container.
+ MIR* p = bottom_block->first_mir_insn;
+ while (p != NULL) {
+ int opcode = p->dalvikInsn.opcode;
+ /*
+ * Some messiness here to ensure that we only enter real opcodes and only the
+ * first half of a potentially throwing instruction that has been split into
+ * CHECK and work portions. The 2nd half of a split operation will have a non-null
+ * throw_insn pointer that refers to the 1st half.
+ */
+ if ((opcode == kMirOpCheck) || (!IsPseudoMirOp(opcode) && (p->meta.throw_insn == NULL))) {
+ dex_pc_to_block_map_.Put(p->offset, bottom_block->id);
+ }
+ p = (p == bottom_block->last_mir_insn) ? NULL : p->next;
+ }
+
return bottom_block;
}
@@ -206,45 +231,43 @@ BasicBlock* MIRGraph::SplitBlock(unsigned int code_offset,
* (by the caller)
* Utilizes a map for fast lookup of the typical cases.
*/
-BasicBlock* MIRGraph::FindBlock(unsigned int code_offset, bool split, bool create,
+BasicBlock* MIRGraph::FindBlock(DexOffset code_offset, bool split, bool create,
BasicBlock** immed_pred_block_p) {
- BasicBlock* bb;
- unsigned int i;
- SafeMap<unsigned int, BasicBlock*>::iterator it;
+ if (code_offset >= cu_->code_item->insns_size_in_code_units_) {
+ return NULL;
+ }
+
+ int block_id = dex_pc_to_block_map_.Get(code_offset);
+ BasicBlock* bb = (block_id == 0) ? NULL : block_list_.Get(block_id);
- it = block_map_.find(code_offset);
- if (it != block_map_.end()) {
- return it->second;
- } else if (!create) {
+ if ((bb != NULL) && (bb->start_offset == code_offset)) {
+ // Does this containing block start with the desired instruction?
+ return bb;
+ }
+
+ // No direct hit.
+ if (!create) {
return NULL;
}
- if (split) {
- for (i = 0; i < block_list_.Size(); i++) {
- bb = block_list_.Get(i);
- if (bb->block_type != kDalvikByteCode) continue;
- /* Check if a branch jumps into the middle of an existing block */
- if ((code_offset > bb->start_offset) && (bb->last_mir_insn != NULL) &&
- (code_offset <= bb->last_mir_insn->offset)) {
- BasicBlock *new_bb = SplitBlock(code_offset, bb, bb == *immed_pred_block_p ?
- immed_pred_block_p : NULL);
- return new_bb;
- }
- }
+ if (bb != NULL) {
+ // The target exists somewhere in an existing block.
+ return SplitBlock(code_offset, bb, bb == *immed_pred_block_p ? immed_pred_block_p : NULL);
}
- /* Create a new one */
+ // Create a new block.
bb = NewMemBB(kDalvikByteCode, num_blocks_++);
block_list_.Insert(bb);
bb->start_offset = code_offset;
- block_map_.Put(bb->start_offset, bb);
+ dex_pc_to_block_map_.Put(bb->start_offset, bb->id);
return bb;
}
+
/* Identify code range in try blocks and set up the empty catch blocks */
void MIRGraph::ProcessTryCatchBlocks() {
int tries_size = current_code_item_->tries_size_;
- int offset;
+ DexOffset offset;
if (tries_size == 0) {
return;
@@ -253,8 +276,8 @@ void MIRGraph::ProcessTryCatchBlocks() {
for (int i = 0; i < tries_size; i++) {
const DexFile::TryItem* pTry =
DexFile::GetTryItems(*current_code_item_, i);
- int start_offset = pTry->start_addr_;
- int end_offset = start_offset + pTry->insn_count_;
+ DexOffset start_offset = pTry->start_addr_;
+ DexOffset end_offset = start_offset + pTry->insn_count_;
for (offset = start_offset; offset < end_offset; offset++) {
try_block_addr_->SetBit(offset);
}
@@ -275,10 +298,10 @@ void MIRGraph::ProcessTryCatchBlocks() {
}
/* Process instructions with the kBranch flag */
-BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
- int flags, const uint16_t* code_ptr,
+BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
+ int width, int flags, const uint16_t* code_ptr,
const uint16_t* code_end) {
- int target = cur_offset;
+ DexOffset target = cur_offset;
switch (insn->dalvikInsn.opcode) {
case Instruction::GOTO:
case Instruction::GOTO_16:
@@ -306,10 +329,11 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur
default:
LOG(FATAL) << "Unexpected opcode(" << insn->dalvikInsn.opcode << ") with kBranch set";
}
+ CountBranch(target);
BasicBlock *taken_block = FindBlock(target, /* split */ true, /* create */ true,
/* immed_pred_block_p */ &cur_block);
- cur_block->taken = taken_block;
- taken_block->predecessors->Insert(cur_block);
+ cur_block->taken = taken_block->id;
+ taken_block->predecessors->Insert(cur_block->id);
/* Always terminate the current block for conditional branches */
if (flags & Instruction::kContinue) {
@@ -331,8 +355,8 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur
true,
/* immed_pred_block_p */
&cur_block);
- cur_block->fall_through = fallthrough_block;
- fallthrough_block->predecessors->Insert(cur_block);
+ cur_block->fall_through = fallthrough_block->id;
+ fallthrough_block->predecessors->Insert(cur_block->id);
} else if (code_ptr < code_end) {
FindBlock(cur_offset + width, /* split */ false, /* create */ true,
/* immed_pred_block_p */ NULL);
@@ -341,7 +365,7 @@ BasicBlock* MIRGraph::ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur
}
/* Process instructions with the kSwitch flag */
-void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags) {
const uint16_t* switch_data =
reinterpret_cast<const uint16_t*>(GetCurrentInsns() + cur_offset + insn->dalvikInsn.vB);
@@ -385,14 +409,13 @@ void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset
first_key = 0; // To make the compiler happy
}
- if (cur_block->successor_block_list.block_list_type != kNotUsed) {
+ if (cur_block->successor_block_list_type != kNotUsed) {
LOG(FATAL) << "Successor block list already in use: "
- << static_cast<int>(cur_block->successor_block_list.block_list_type);
+ << static_cast<int>(cur_block->successor_block_list_type);
}
- cur_block->successor_block_list.block_list_type =
- (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
- kPackedSwitch : kSparseSwitch;
- cur_block->successor_block_list.blocks =
+ cur_block->successor_block_list_type =
+ (insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ? kPackedSwitch : kSparseSwitch;
+ cur_block->successor_blocks =
new (arena_) GrowableArray<SuccessorBlockInfo*>(arena_, size, kGrowableArraySuccessorBlocks);
for (i = 0; i < size; i++) {
@@ -401,24 +424,24 @@ void MIRGraph::ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset
SuccessorBlockInfo *successor_block_info =
static_cast<SuccessorBlockInfo*>(arena_->Alloc(sizeof(SuccessorBlockInfo),
ArenaAllocator::kAllocSuccessor));
- successor_block_info->block = case_block;
+ successor_block_info->block = case_block->id;
successor_block_info->key =
(insn->dalvikInsn.opcode == Instruction::PACKED_SWITCH) ?
first_key + i : keyTable[i];
- cur_block->successor_block_list.blocks->Insert(successor_block_info);
- case_block->predecessors->Insert(cur_block);
+ cur_block->successor_blocks->Insert(successor_block_info);
+ case_block->predecessors->Insert(cur_block->id);
}
/* Fall-through case */
BasicBlock* fallthrough_block = FindBlock(cur_offset + width, /* split */ false,
/* create */ true, /* immed_pred_block_p */ NULL);
- cur_block->fall_through = fallthrough_block;
- fallthrough_block->predecessors->Insert(cur_block);
+ cur_block->fall_through = fallthrough_block->id;
+ fallthrough_block->predecessors->Insert(cur_block->id);
}
/* Process instructions with the kThrow flag */
-BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
- int flags, ArenaBitVector* try_block_addr,
+BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset,
+ int width, int flags, ArenaBitVector* try_block_addr,
const uint16_t* code_ptr, const uint16_t* code_end) {
bool in_try_block = try_block_addr->IsBitSet(cur_offset);
@@ -426,14 +449,14 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_
if (in_try_block) {
CatchHandlerIterator iterator(*current_code_item_, cur_offset);
- if (cur_block->successor_block_list.block_list_type != kNotUsed) {
+ if (cur_block->successor_block_list_type != kNotUsed) {
LOG(INFO) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
LOG(FATAL) << "Successor block list already in use: "
- << static_cast<int>(cur_block->successor_block_list.block_list_type);
+ << static_cast<int>(cur_block->successor_block_list_type);
}
- cur_block->successor_block_list.block_list_type = kCatch;
- cur_block->successor_block_list.blocks =
+ cur_block->successor_block_list_type = kCatch;
+ cur_block->successor_blocks =
new (arena_) GrowableArray<SuccessorBlockInfo*>(arena_, 2, kGrowableArraySuccessorBlocks);
for (; iterator.HasNext(); iterator.Next()) {
@@ -445,17 +468,17 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_
}
SuccessorBlockInfo *successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
(arena_->Alloc(sizeof(SuccessorBlockInfo), ArenaAllocator::kAllocSuccessor));
- successor_block_info->block = catch_block;
+ successor_block_info->block = catch_block->id;
successor_block_info->key = iterator.GetHandlerTypeIndex();
- cur_block->successor_block_list.blocks->Insert(successor_block_info);
- catch_block->predecessors->Insert(cur_block);
+ cur_block->successor_blocks->Insert(successor_block_info);
+ catch_block->predecessors->Insert(cur_block->id);
}
} else {
BasicBlock *eh_block = NewMemBB(kExceptionHandling, num_blocks_++);
- cur_block->taken = eh_block;
+ cur_block->taken = eh_block->id;
block_list_.Insert(eh_block);
eh_block->start_offset = cur_offset;
- eh_block->predecessors->Insert(cur_block);
+ eh_block->predecessors->Insert(cur_block->id);
}
if (insn->dalvikInsn.opcode == Instruction::THROW) {
@@ -484,12 +507,15 @@ BasicBlock* MIRGraph::ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_
* pseudo exception edge MIR. Note also that this new block is
* not automatically terminated after the work portion, and may
* contain following instructions.
+ *
+ * Note also that the dex_pc_to_block_map_ entry for the potentially
+ * throwing instruction will refer to the original basic block.
*/
BasicBlock *new_block = NewMemBB(kDalvikByteCode, num_blocks_++);
block_list_.Insert(new_block);
new_block->start_offset = insn->offset;
- cur_block->fall_through = new_block;
- new_block->predecessors->Insert(cur_block);
+ cur_block->fall_through = new_block->id;
+ new_block->predecessors->Insert(cur_block->id);
MIR* new_insn = static_cast<MIR*>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR));
*new_insn = *insn;
insn->dalvikInsn.opcode =
@@ -517,7 +543,10 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
current_code_item_->insns_ + current_code_item_->insns_size_in_code_units_;
// TODO: need to rework expansion of block list & try_block_addr when inlining activated.
+ // TUNING: use better estimate of basic blocks for following resize.
block_list_.Resize(block_list_.Size() + current_code_item_->insns_size_in_code_units_);
+ dex_pc_to_block_map_.SetSize(dex_pc_to_block_map_.Size() + current_code_item_->insns_size_in_code_units_);
+
// TODO: replace with explicit resize routine. Using automatic extension side effect for now.
try_block_addr_->SetBit(current_code_item_->insns_size_in_code_units_);
try_block_addr_->ClearBit(current_code_item_->insns_size_in_code_units_);
@@ -527,9 +556,14 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
DCHECK(entry_block_ == NULL);
DCHECK(exit_block_ == NULL);
DCHECK_EQ(num_blocks_, 0);
+ // Use id 0 to represent a null block.
+ BasicBlock* null_block = NewMemBB(kNullBlock, num_blocks_++);
+ DCHECK_EQ(null_block->id, NullBasicBlockId);
+ null_block->hidden = true;
+ block_list_.Insert(null_block);
entry_block_ = NewMemBB(kEntryBlock, num_blocks_++);
- exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
block_list_.Insert(entry_block_);
+ exit_block_ = NewMemBB(kExitBlock, num_blocks_++);
block_list_.Insert(exit_block_);
// TODO: deprecate all "cu->" fields; move what's left to wherever CompilationUnit is allocated.
cu_->dex_file = &dex_file;
@@ -554,15 +588,12 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
/* Current block to record parsed instructions */
BasicBlock *cur_block = NewMemBB(kDalvikByteCode, num_blocks_++);
- DCHECK_EQ(current_offset_, 0);
+ DCHECK_EQ(current_offset_, 0U);
cur_block->start_offset = current_offset_;
block_list_.Insert(cur_block);
- /* Add first block to the fast lookup cache */
-// FIXME: block map needs association with offset/method pair rather than just offset
- block_map_.Put(cur_block->start_offset, cur_block);
-// FIXME: this needs to insert at the insert point rather than entry block.
- entry_block_->fall_through = cur_block;
- cur_block->predecessors->Insert(entry_block_);
+ // TODO: for inlining support, insert at the insert point rather than entry block.
+ entry_block_->fall_through = cur_block->id;
+ cur_block->predecessors->Insert(entry_block_->id);
/* Identify code range in try blocks and set up the empty catch blocks */
ProcessTryCatchBlocks();
@@ -586,7 +617,6 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
opcode_count_[static_cast<int>(opcode)]++;
}
-
/* Possible simple method? */
if (live_pattern) {
live_pattern = false;
@@ -628,8 +658,8 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
// It is a simple nop - treat normally.
AppendMIR(cur_block, insn);
} else {
- DCHECK(cur_block->fall_through == NULL);
- DCHECK(cur_block->taken == NULL);
+ DCHECK(cur_block->fall_through == NullBasicBlockId);
+ DCHECK(cur_block->taken == NullBasicBlockId);
// Unreachable instruction, mark for no continuation.
flags &= ~Instruction::kContinue;
}
@@ -637,6 +667,9 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
AppendMIR(cur_block, insn);
}
+ // Associate the starting dex_pc for this opcode with its containing basic block.
+ dex_pc_to_block_map_.Put(insn->offset, cur_block->id);
+
code_ptr += width;
if (flags & Instruction::kBranch) {
@@ -644,8 +677,8 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
width, flags, code_ptr, code_end);
} else if (flags & Instruction::kReturn) {
cur_block->terminated_by_return = true;
- cur_block->fall_through = exit_block_;
- exit_block_->predecessors->Insert(cur_block);
+ cur_block->fall_through = exit_block_->id;
+ exit_block_->predecessors->Insert(cur_block->id);
/*
* Terminate the current block if there are instructions
* afterwards.
@@ -674,13 +707,13 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_
* instruction is not an unconditional branch, connect them through
* the fall-through link.
*/
- DCHECK(cur_block->fall_through == NULL ||
- cur_block->fall_through == next_block ||
- cur_block->fall_through == exit_block_);
+ DCHECK(cur_block->fall_through == NullBasicBlockId ||
+ GetBasicBlock(cur_block->fall_through) == next_block ||
+ GetBasicBlock(cur_block->fall_through) == exit_block_);
- if ((cur_block->fall_through == NULL) && (flags & Instruction::kContinue)) {
- cur_block->fall_through = next_block;
- next_block->predecessors->Insert(cur_block);
+ if ((cur_block->fall_through == NullBasicBlockId) && (flags & Instruction::kContinue)) {
+ cur_block->fall_through = next_block->id;
+ next_block->predecessors->Insert(cur_block->id);
}
cur_block = next_block;
}
@@ -712,7 +745,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) {
std::string fname(PrettyMethod(cu_->method_idx, *cu_->dex_file));
ReplaceSpecialChars(fname);
fname = StringPrintf("%s%s%x.dot", dir_prefix, fname.c_str(),
- GetEntryBlock()->fall_through->start_offset);
+ GetBasicBlock(GetEntryBlock()->fall_through)->start_offset);
file = fopen(fname.c_str(), "w");
if (file == NULL) {
return;
@@ -759,31 +792,30 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) {
char block_name1[BLOCK_NAME_LEN], block_name2[BLOCK_NAME_LEN];
- if (bb->taken) {
+ if (bb->taken != NullBasicBlockId) {
GetBlockName(bb, block_name1);
- GetBlockName(bb->taken, block_name2);
+ GetBlockName(GetBasicBlock(bb->taken), block_name2);
fprintf(file, " %s:s -> %s:n [style=dotted]\n",
block_name1, block_name2);
}
- if (bb->fall_through) {
+ if (bb->fall_through != NullBasicBlockId) {
GetBlockName(bb, block_name1);
- GetBlockName(bb->fall_through, block_name2);
+ GetBlockName(GetBasicBlock(bb->fall_through), block_name2);
fprintf(file, " %s:s -> %s:n\n", block_name1, block_name2);
}
- if (bb->successor_block_list.block_list_type != kNotUsed) {
+ if (bb->successor_block_list_type != kNotUsed) {
fprintf(file, " succ%04x_%d [shape=%s,label = \"{ \\\n",
bb->start_offset, bb->id,
- (bb->successor_block_list.block_list_type == kCatch) ?
- "Mrecord" : "record");
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ (bb->successor_block_list_type == kCatch) ? "Mrecord" : "record");
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
SuccessorBlockInfo *successor_block_info = iterator.Next();
int succ_id = 0;
while (true) {
if (successor_block_info == NULL) break;
- BasicBlock *dest_block = successor_block_info->block;
+ BasicBlock *dest_block = GetBasicBlock(successor_block_info->block);
SuccessorBlockInfo *next_successor_block_info = iterator.Next();
fprintf(file, " {<f%d> %04x: %04x\\l}%s\\\n",
@@ -800,16 +832,16 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) {
fprintf(file, " %s:s -> succ%04x_%d:n [style=dashed]\n",
block_name1, bb->start_offset, bb->id);
- if (bb->successor_block_list.block_list_type == kPackedSwitch ||
- bb->successor_block_list.block_list_type == kSparseSwitch) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks);
+ if (bb->successor_block_list_type == kPackedSwitch ||
+ bb->successor_block_list_type == kSparseSwitch) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
succ_id = 0;
while (true) {
SuccessorBlockInfo *successor_block_info = iter.Next();
if (successor_block_info == NULL) break;
- BasicBlock *dest_block = successor_block_info->block;
+ BasicBlock* dest_block = GetBasicBlock(successor_block_info->block);
GetBlockName(dest_block, block_name2);
fprintf(file, " succ%04x_%d:f%d:e -> %s:n\n", bb->start_offset,
@@ -825,7 +857,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks) {
fprintf(file, " cfg%s [label=\"%s\", shape=none];\n",
block_name1, block_name1);
if (bb->i_dom) {
- GetBlockName(bb->i_dom, block_name2);
+ GetBlockName(GetBasicBlock(bb->i_dom), block_name2);
fprintf(file, " cfg%s:s -> cfg%s:n\n\n", block_name2, block_name1);
}
}
@@ -839,10 +871,9 @@ void MIRGraph::AppendMIR(BasicBlock* bb, MIR* mir) {
if (bb->first_mir_insn == NULL) {
DCHECK(bb->last_mir_insn == NULL);
bb->last_mir_insn = bb->first_mir_insn = mir;
- mir->prev = mir->next = NULL;
+ mir->next = NULL;
} else {
bb->last_mir_insn->next = mir;
- mir->prev = bb->last_mir_insn;
mir->next = NULL;
bb->last_mir_insn = mir;
}
@@ -853,25 +884,19 @@ void MIRGraph::PrependMIR(BasicBlock* bb, MIR* mir) {
if (bb->first_mir_insn == NULL) {
DCHECK(bb->last_mir_insn == NULL);
bb->last_mir_insn = bb->first_mir_insn = mir;
- mir->prev = mir->next = NULL;
+ mir->next = NULL;
} else {
- bb->first_mir_insn->prev = mir;
mir->next = bb->first_mir_insn;
- mir->prev = NULL;
bb->first_mir_insn = mir;
}
}
/* Insert a MIR instruction after the specified MIR */
void MIRGraph::InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir) {
- new_mir->prev = current_mir;
new_mir->next = current_mir->next;
current_mir->next = new_mir;
- if (new_mir->next) {
- /* Is not the last MIR in the block */
- new_mir->next->prev = new_mir;
- } else {
+ if (bb->last_mir_insn == current_mir) {
/* Is the last MIR in the block */
bb->last_mir_insn = new_mir;
}
@@ -901,8 +926,9 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) {
opcode = insn.opcode;
} else if (opcode == kMirOpNop) {
str.append("[");
- insn.opcode = mir->meta.original_opcode;
- opcode = mir->meta.original_opcode;
+ // Recover original opcode.
+ insn.opcode = Instruction::At(current_code_item_->insns_ + mir->offset)->Opcode();
+ opcode = insn.opcode;
nop = true;
}
@@ -915,7 +941,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) {
}
if (opcode == kMirOpPhi) {
- int* incoming = reinterpret_cast<int*>(insn.vB);
+ BasicBlockId* incoming = mir->meta.phi_incoming;
str.append(StringPrintf(" %s = (%s",
GetSSANameWithConst(ssa_rep->defs[0], true).c_str(),
GetSSANameWithConst(ssa_rep->uses[0], true).c_str()));
@@ -1065,7 +1091,7 @@ void MIRGraph::GetBlockName(BasicBlock* bb, char* name) {
}
const char* MIRGraph::GetShortyFromTargetIdx(int target_idx) {
- // FIXME: use current code unit for inline support.
+ // TODO: for inlining support, use current code unit.
const DexFile::MethodId& method_id = cu_->dex_file->GetMethodId(target_idx);
return cu_->dex_file->GetShorty(method_id.proto_idx_);
}
@@ -1095,13 +1121,13 @@ void MIRGraph::DumpMIRGraph() {
bb->start_offset,
bb->last_mir_insn ? bb->last_mir_insn->offset : bb->start_offset,
bb->last_mir_insn ? "" : " empty");
- if (bb->taken) {
- LOG(INFO) << " Taken branch: block " << bb->taken->id
- << "(0x" << std::hex << bb->taken->start_offset << ")";
+ if (bb->taken != NullBasicBlockId) {
+ LOG(INFO) << " Taken branch: block " << bb->taken
+ << "(0x" << std::hex << GetBasicBlock(bb->taken)->start_offset << ")";
}
- if (bb->fall_through) {
- LOG(INFO) << " Fallthrough : block " << bb->fall_through->id
- << " (0x" << std::hex << bb->fall_through->start_offset << ")";
+ if (bb->fall_through != NullBasicBlockId) {
+ LOG(INFO) << " Fallthrough : block " << bb->fall_through
+ << " (0x" << std::hex << GetBasicBlock(bb->fall_through)->start_offset << ")";
}
}
}
@@ -1121,7 +1147,6 @@ CallInfo* MIRGraph::NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type,
info->result.location = kLocInvalid;
} else {
info->result = GetRawDest(move_result_mir);
- move_result_mir->meta.original_opcode = move_result_mir->dalvikInsn.opcode;
move_result_mir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
}
info->num_arg_words = mir->ssa_rep->num_uses;
@@ -1145,10 +1170,10 @@ BasicBlock* MIRGraph::NewMemBB(BBType block_type, int block_id) {
bb->block_type = block_type;
bb->id = block_id;
// TUNING: better estimate of the exit block predecessors?
- bb->predecessors = new (arena_) GrowableArray<BasicBlock*>(arena_,
+ bb->predecessors = new (arena_) GrowableArray<BasicBlockId>(arena_,
(block_type == kExitBlock) ? 2048 : 2,
kGrowableArrayPredecessors);
- bb->successor_block_list.block_list_type = kNotUsed;
+ bb->successor_block_list_type = kNotUsed;
block_id_map_.Put(block_id, block_id);
return bb;
}
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 6f8bd85630..a69dde0da3 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -149,7 +149,7 @@ enum DataFlowAttributePos {
#define DF_C_IS_REG (DF_UC)
#define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER)
#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C)
-
+#define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)
enum OatMethodAttributes {
kIsLeaf, // Method is leaf.
kHasLoop, // Method contains simple loop.
@@ -183,6 +183,9 @@ enum OatMethodAttributes {
#define BLOCK_NAME_LEN 80
+typedef uint16_t BasicBlockId;
+static const BasicBlockId NullBasicBlockId = 0;
+
/*
* In general, vreg/sreg describe Dalvik registers that originated with dx. However,
* it is useful to have compiler-generated temporary registers and have them treated
@@ -190,15 +193,15 @@ enum OatMethodAttributes {
* name of compiler-introduced temporaries.
*/
struct CompilerTemp {
- int s_reg;
+ int32_t s_reg;
};
// When debug option enabled, records effectiveness of null and range check elimination.
struct Checkstats {
- int null_checks;
- int null_checks_eliminated;
- int range_checks;
- int range_checks_eliminated;
+ int32_t null_checks;
+ int32_t null_checks_eliminated;
+ int32_t range_checks;
+ int32_t range_checks_eliminated;
};
// Dataflow attributes of a basic block.
@@ -207,7 +210,7 @@ struct BasicBlockDataFlow {
ArenaBitVector* def_v;
ArenaBitVector* live_in_v;
ArenaBitVector* phi_v;
- int* vreg_to_ssa_map;
+ int32_t* vreg_to_ssa_map;
ArenaBitVector* ending_null_check_v;
};
@@ -220,11 +223,11 @@ struct BasicBlockDataFlow {
* we may want to revisit in the future.
*/
struct SSARepresentation {
- int num_uses;
- int* uses;
+ int16_t num_uses;
+ int16_t num_defs;
+ int32_t* uses;
bool* fp_use;
- int num_defs;
- int* defs;
+ int32_t* defs;
bool* fp_def;
};
@@ -233,51 +236,53 @@ struct SSARepresentation {
* wrapper around a Dalvik byte code.
*/
struct MIR {
+ /*
+ * TODO: remove embedded DecodedInstruction to save space, keeping only opcode. Recover
+ * additional fields on as-needed basis. Question: how to support MIR Pseudo-ops; probably
+ * need to carry aux data pointer.
+ */
DecodedInstruction dalvikInsn;
- uint32_t width; // NOTE: only need 16 bits for width.
- unsigned int offset;
- int m_unit_index; // From which method was this MIR included
- MIR* prev;
+ uint16_t width; // Note: width can include switch table or fill array data.
+ NarrowDexOffset offset; // Offset of the instruction in code units.
+ uint16_t optimization_flags;
+ int16_t m_unit_index; // From which method was this MIR included
MIR* next;
SSARepresentation* ssa_rep;
- int optimization_flags;
union {
+ // Incoming edges for phi node.
+ BasicBlockId* phi_incoming;
// Establish link between two halves of throwing instructions.
MIR* throw_insn;
- // Saved opcode for NOP'd MIRs
- Instruction::Code original_opcode;
} meta;
};
struct SuccessorBlockInfo;
struct BasicBlock {
- int id;
- int dfs_id;
- bool visited;
- bool hidden;
- bool catch_entry;
- bool explicit_throw;
- bool conditional_branch;
- bool terminated_by_return; // Block ends with a Dalvik return opcode.
- bool dominates_return; // Is a member of return extended basic block.
- uint16_t start_offset;
+ BasicBlockId id;
+ BasicBlockId dfs_id;
+ NarrowDexOffset start_offset; // Offset in code units.
+ BasicBlockId fall_through;
+ BasicBlockId taken;
+ BasicBlockId i_dom; // Immediate dominator.
uint16_t nesting_depth;
- BBType block_type;
+ BBType block_type:4;
+ BlockListType successor_block_list_type:4;
+ bool visited:1;
+ bool hidden:1;
+ bool catch_entry:1;
+ bool explicit_throw:1;
+ bool conditional_branch:1;
+ bool terminated_by_return:1; // Block ends with a Dalvik return opcode.
+ bool dominates_return:1; // Is a member of return extended basic block.
MIR* first_mir_insn;
MIR* last_mir_insn;
- BasicBlock* fall_through;
- BasicBlock* taken;
- BasicBlock* i_dom; // Immediate dominator.
BasicBlockDataFlow* data_flow_info;
- GrowableArray<BasicBlock*>* predecessors;
ArenaBitVector* dominators;
ArenaBitVector* i_dominated; // Set nodes being immediately dominated.
ArenaBitVector* dom_frontier; // Dominance frontier.
- struct { // For one-to-many successors like.
- BlockListType block_list_type; // switch and exception handling.
- GrowableArray<SuccessorBlockInfo*>* blocks;
- } successor_block_list;
+ GrowableArray<BasicBlockId>* predecessors;
+ GrowableArray<SuccessorBlockInfo*>* successor_blocks;
};
/*
@@ -285,9 +290,8 @@ struct BasicBlock {
* "SuccessorBlockInfo". For catch blocks, key is type index for the exception. For swtich
* blocks, key is the case value.
*/
-// TODO: make class with placement new.
struct SuccessorBlockInfo {
- BasicBlock* block;
+ BasicBlockId block;
int key;
};
@@ -296,6 +300,15 @@ struct SuccessorBlockInfo {
* the type of an SSA name (and, can also be used by code generators to record where the
* value is located (i.e. - physical register, frame, spill, etc.). For each SSA name (SReg)
* there is a RegLocation.
+ * A note on SSA names:
+ * o SSA names for Dalvik vRegs v0..vN will be assigned 0..N. These represent the "vN_0"
+ * names. Negative SSA names represent special values not present in the Dalvik byte code.
+ * For example, SSA name -1 represents an invalid SSA name, and SSA name -2 represents the
+ * the Method pointer. SSA names < -2 are reserved for future use.
+ * o The vN_0 names for non-argument Dalvik should in practice never be used (as they would
+ * represent the read of an undefined local variable). The first definition of the
+ * underlying Dalvik vReg will result in a vN_1 name.
+ *
* FIXME: The orig_sreg field was added as a workaround for llvm bitcode generation. With
* the latest restructuring, we should be able to remove it and rely on s_reg_low throughout.
*/
@@ -311,9 +324,9 @@ struct RegLocation {
unsigned home:1; // Does this represent the home location?
uint8_t low_reg; // First physical register.
uint8_t high_reg; // 2nd physical register (if wide).
- int32_t s_reg_low; // SSA name for low Dalvik word.
- int32_t orig_sreg; // TODO: remove after Bitcode gen complete
- // and consolodate usage w/ s_reg_low.
+ int16_t s_reg_low; // SSA name for low Dalvik word.
+ int16_t orig_sreg; // TODO: remove after Bitcode gen complete
+ // and consolidate usage w/ s_reg_low.
};
/*
@@ -334,7 +347,7 @@ struct CallInfo {
RegLocation target; // Target of following move_result.
bool skip_this;
bool is_range;
- int offset; // Dalvik offset.
+ DexOffset offset; // Offset in code units.
};
@@ -361,7 +374,7 @@ class MIRGraph {
uint32_t method_idx, jobject class_loader, const DexFile& dex_file);
/* Find existing block */
- BasicBlock* FindBlock(unsigned int code_offset) {
+ BasicBlock* FindBlock(DexOffset code_offset) {
return FindBlock(code_offset, false, false, NULL);
}
@@ -394,7 +407,7 @@ class MIRGraph {
}
BasicBlock* GetBasicBlock(int block_id) const {
- return block_list_.Get(block_id);
+ return (block_id == NullBasicBlockId) ? NULL : block_list_.Get(block_id);
}
size_t GetBasicBlockListCount() const {
@@ -405,15 +418,15 @@ class MIRGraph {
return &block_list_;
}
- GrowableArray<int>* GetDfsOrder() {
+ GrowableArray<BasicBlockId>* GetDfsOrder() {
return dfs_order_;
}
- GrowableArray<int>* GetDfsPostOrder() {
+ GrowableArray<BasicBlockId>* GetDfsPostOrder() {
return dfs_post_order_;
}
- GrowableArray<int>* GetDomPostOrder() {
+ GrowableArray<BasicBlockId>* GetDomPostOrder() {
return dom_post_order_traversal_;
}
@@ -477,6 +490,12 @@ class MIRGraph {
}
void SetNumSSARegs(int new_num) {
+ /*
+ * TODO: It's theoretically possible to exceed 32767, though any cases which did
+ * would be filtered out with current settings. When orig_sreg field is removed
+ * from RegLocation, expand s_reg_low to handle all possible cases and remove DCHECK().
+ */
+ DCHECK_EQ(new_num, static_cast<int16_t>(new_num));
num_ssa_regs_ = new_num;
}
@@ -561,14 +580,35 @@ class MIRGraph {
return special_case_;
}
- bool IsBackedge(BasicBlock* branch_bb, BasicBlock* target_bb) {
- return ((target_bb != NULL) && (target_bb->start_offset <= branch_bb->start_offset));
+ bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
+ return ((target_bb_id != NullBasicBlockId) &&
+ (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset));
}
bool IsBackwardsBranch(BasicBlock* branch_bb) {
return IsBackedge(branch_bb, branch_bb->taken) || IsBackedge(branch_bb, branch_bb->fall_through);
}
+ void CountBranch(DexOffset target_offset) {
+ if (target_offset <= current_offset_) {
+ backward_branches_++;
+ } else {
+ forward_branches_++;
+ }
+ }
+
+ int GetBranchCount() {
+ return backward_branches_ + forward_branches_;
+ }
+
+ bool IsPseudoMirOp(Instruction::Code opcode) {
+ return static_cast<int>(opcode) >= static_cast<int>(kMirOpFirst);
+ }
+
+ bool IsPseudoMirOp(int opcode) {
+ return opcode >= static_cast<int>(kMirOpFirst);
+ }
+
void BasicBlockCombine();
void CodeLayout();
void DumpCheckStats();
@@ -580,11 +620,34 @@ class MIRGraph {
void SSATransformation();
void CheckForDominanceFrontier(BasicBlock* dom_bb, const BasicBlock* succ_bb);
void NullCheckElimination();
+ /*
+ * Type inference handling helpers. Because Dalvik's bytecode is not fully typed,
+ * we have to do some work to figure out the sreg type. For some operations it is
+ * clear based on the opcode (i.e. ADD_FLOAT v0, v1, v2), but for others (MOVE), we
+ * may never know the "real" type.
+ *
+ * We perform the type inference operation by using an iterative walk over
+ * the graph, propagating types "defined" by typed opcodes to uses and defs in
+ * non-typed opcodes (such as MOVE). The Setxx(index) helpers are used to set defined
+ * types on typed opcodes (such as ADD_INT). The Setxx(index, is_xx) form is used to
+ * propagate types through non-typed opcodes such as PHI and MOVE. The is_xx flag
+ * tells whether our guess of the type is based on a previously typed definition.
+ * If so, the defined type takes precedence. Note that it's possible to have the same sreg
+ * show multiple defined types because dx treats constants as untyped bit patterns.
+ * The return value of the Setxx() helpers says whether or not the Setxx() action changed
+ * the current guess, and is used to know when to terminate the iterative walk.
+ */
bool SetFp(int index, bool is_fp);
+ bool SetFp(int index);
bool SetCore(int index, bool is_core);
+ bool SetCore(int index);
bool SetRef(int index, bool is_ref);
+ bool SetRef(int index);
bool SetWide(int index, bool is_wide);
+ bool SetWide(int index);
bool SetHigh(int index, bool is_high);
+ bool SetHigh(int index);
+
void AppendMIR(BasicBlock* bb, MIR* mir);
void PrependMIR(BasicBlock* bb, MIR* mir);
void InsertMIRAfter(BasicBlock* bb, MIR* current_mir, MIR* new_mir);
@@ -597,6 +660,9 @@ class MIRGraph {
void DumpMIRGraph();
CallInfo* NewMemCallInfo(BasicBlock* bb, MIR* mir, InvokeType type, bool is_range);
BasicBlock* NewMemBB(BBType block_type, int block_id);
+ MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir);
+ BasicBlock* NextDominatedBlock(BasicBlock* bb);
+ bool LayoutBlocks(BasicBlock* bb);
/*
* IsDebugBuild sanity check: keep track of the Dex PCs for catch entries so that later on
@@ -625,15 +691,16 @@ class MIRGraph {
bool InvokeUsesMethodStar(MIR* mir);
int ParseInsn(const uint16_t* code_ptr, DecodedInstruction* decoded_instruction);
bool ContentIsInsn(const uint16_t* code_ptr);
- BasicBlock* SplitBlock(unsigned int code_offset, BasicBlock* orig_block,
+ BasicBlock* SplitBlock(DexOffset code_offset, BasicBlock* orig_block,
BasicBlock** immed_pred_block_p);
- BasicBlock* FindBlock(unsigned int code_offset, bool split, bool create,
+ BasicBlock* FindBlock(DexOffset code_offset, bool split, bool create,
BasicBlock** immed_pred_block_p);
void ProcessTryCatchBlocks();
- BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ BasicBlock* ProcessCanBranch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags, const uint16_t* code_ptr, const uint16_t* code_end);
- void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, int cur_offset, int width, int flags);
- BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, int cur_offset, int width,
+ void ProcessCanSwitch(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
+ int flags);
+ BasicBlock* ProcessCanThrow(BasicBlock* cur_block, MIR* insn, DexOffset cur_offset, int width,
int flags, ArenaBitVector* try_block_addr, const uint16_t* code_ptr,
const uint16_t* code_end);
int AddNewSReg(int v_reg);
@@ -689,9 +756,9 @@ class MIRGraph {
GrowableArray<uint32_t> use_counts_; // Weighted by nesting depth
GrowableArray<uint32_t> raw_use_counts_; // Not weighted
unsigned int num_reachable_blocks_;
- GrowableArray<int>* dfs_order_;
- GrowableArray<int>* dfs_post_order_;
- GrowableArray<int>* dom_post_order_traversal_;
+ GrowableArray<BasicBlockId>* dfs_order_;
+ GrowableArray<BasicBlockId>* dfs_post_order_;
+ GrowableArray<BasicBlockId>* dom_post_order_traversal_;
int* i_dom_list_;
ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks.
ArenaBitVector* temp_block_v_;
@@ -702,24 +769,25 @@ class MIRGraph {
ArenaBitVector* try_block_addr_;
BasicBlock* entry_block_;
BasicBlock* exit_block_;
- BasicBlock* cur_block_;
int num_blocks_;
const DexFile::CodeItem* current_code_item_;
- SafeMap<unsigned int, BasicBlock*> block_map_; // FindBlock lookup cache.
+ GrowableArray<uint16_t> dex_pc_to_block_map_; // FindBlock lookup cache.
std::vector<DexCompilationUnit*> m_units_; // List of methods included in this graph
typedef std::pair<int, int> MIRLocation; // Insert point, (m_unit_ index, offset)
std::vector<MIRLocation> method_stack_; // Include stack
int current_method_;
- int current_offset_;
+ DexOffset current_offset_; // Offset in code units
int def_count_; // Used to estimate size of ssa name storage.
int* opcode_count_; // Dex opcode coverage stats.
int num_ssa_regs_; // Number of names following SSA transformation.
- std::vector<BasicBlock*> extended_basic_blocks_; // Heads of block "traces".
+ std::vector<BasicBlockId> extended_basic_blocks_; // Heads of block "traces".
int method_sreg_;
unsigned int attributes_;
Checkstats* checkstats_;
SpecialCaseHandler special_case_;
ArenaAllocator* arena_;
+ int backward_branches_;
+ int forward_branches_;
};
} // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index b7611f8f5b..f5913a5ad4 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -96,19 +96,19 @@ void MIRGraph::PropagateConstants() {
is_constant_v_ = new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false);
constant_values_ = static_cast<int*>(arena_->Alloc(sizeof(int) * GetNumSSARegs(),
ArenaAllocator::kAllocDFInfo));
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
DoConstantPropogation(bb);
}
}
/* Advance to next strictly dominated MIR node in an extended basic block */
-static MIR* AdvanceMIR(BasicBlock** p_bb, MIR* mir) {
+MIR* MIRGraph::AdvanceMIR(BasicBlock** p_bb, MIR* mir) {
BasicBlock* bb = *p_bb;
if (mir != NULL) {
mir = mir->next;
if (mir == NULL) {
- bb = bb->fall_through;
+ bb = GetBasicBlock(bb->fall_through);
if ((bb == NULL) || Predecessors(bb) != 1) {
mir = NULL;
} else {
@@ -147,19 +147,21 @@ MIR* MIRGraph::FindMoveResult(BasicBlock* bb, MIR* mir) {
return mir;
}
-static BasicBlock* NextDominatedBlock(BasicBlock* bb) {
+BasicBlock* MIRGraph::NextDominatedBlock(BasicBlock* bb) {
if (bb->block_type == kDead) {
return NULL;
}
DCHECK((bb->block_type == kEntryBlock) || (bb->block_type == kDalvikByteCode)
|| (bb->block_type == kExitBlock));
- if (((bb->taken != NULL) && (bb->fall_through == NULL)) &&
- ((bb->taken->block_type == kDalvikByteCode) || (bb->taken->block_type == kExitBlock))) {
+ BasicBlock* bb_taken = GetBasicBlock(bb->taken);
+ BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through);
+ if (((bb_taken != NULL) && (bb_fall_through == NULL)) &&
+ ((bb_taken->block_type == kDalvikByteCode) || (bb_taken->block_type == kExitBlock))) {
// Follow simple unconditional branches.
- bb = bb->taken;
+ bb = bb_taken;
} else {
// Follow simple fallthrough
- bb = (bb->taken != NULL) ? NULL : bb->fall_through;
+ bb = (bb_taken != NULL) ? NULL : bb_fall_through;
}
if (bb == NULL || (Predecessors(bb) != 1)) {
return NULL;
@@ -311,11 +313,13 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
case Instruction::IF_GTZ:
case Instruction::IF_LEZ:
// If we've got a backwards branch to return, no need to suspend check.
- if ((IsBackedge(bb, bb->taken) && bb->taken->dominates_return) ||
- (IsBackedge(bb, bb->fall_through) && bb->fall_through->dominates_return)) {
+ if ((IsBackedge(bb, bb->taken) && GetBasicBlock(bb->taken)->dominates_return) ||
+ (IsBackedge(bb, bb->fall_through) &&
+ GetBasicBlock(bb->fall_through)->dominates_return)) {
mir->optimization_flags |= MIR_IGNORE_SUSPEND_CHECK;
if (cu_->verbose) {
- LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex << mir->offset;
+ LOG(INFO) << "Suppressed suspend check on branch to return at 0x" << std::hex
+ << mir->offset;
}
}
break;
@@ -328,15 +332,15 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
if (!(cu_->compiler_backend == kPortable) && (cu_->instruction_set == kThumb2) &&
((mir->dalvikInsn.opcode == Instruction::IF_EQZ) ||
(mir->dalvikInsn.opcode == Instruction::IF_NEZ))) {
- BasicBlock* ft = bb->fall_through;
+ BasicBlock* ft = GetBasicBlock(bb->fall_through);
DCHECK(ft != NULL);
- BasicBlock* ft_ft = ft->fall_through;
- BasicBlock* ft_tk = ft->taken;
+ BasicBlock* ft_ft = GetBasicBlock(ft->fall_through);
+ BasicBlock* ft_tk = GetBasicBlock(ft->taken);
- BasicBlock* tk = bb->taken;
+ BasicBlock* tk = GetBasicBlock(bb->taken);
DCHECK(tk != NULL);
- BasicBlock* tk_ft = tk->fall_through;
- BasicBlock* tk_tk = tk->taken;
+ BasicBlock* tk_ft = GetBasicBlock(tk->fall_through);
+ BasicBlock* tk_tk = GetBasicBlock(tk->taken);
/*
* In the select pattern, the taken edge goes to a block that unconditionally
@@ -434,7 +438,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
int dead_def = if_false->ssa_rep->defs[0];
int live_def = if_true->ssa_rep->defs[0];
mir->ssa_rep->defs[0] = live_def;
- int* incoming = reinterpret_cast<int*>(phi->dalvikInsn.vB);
+ BasicBlockId* incoming = phi->meta.phi_incoming;
for (int i = 0; i < phi->ssa_rep->num_uses; i++) {
if (phi->ssa_rep->uses[i] == live_def) {
incoming[i] = bb->id;
@@ -449,7 +453,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
}
}
phi->ssa_rep->num_uses--;
- bb->taken = NULL;
+ bb->taken = NullBasicBlockId;
tk->block_type = kDead;
for (MIR* tmir = ft->first_mir_insn; tmir != NULL; tmir = tmir->next) {
tmir->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
@@ -500,7 +504,7 @@ void MIRGraph::CountChecks(struct BasicBlock* bb) {
}
/* Try to make common case the fallthrough path */
-static bool LayoutBlocks(struct BasicBlock* bb) {
+bool MIRGraph::LayoutBlocks(BasicBlock* bb) {
// TODO: For now, just looking for direct throws. Consider generalizing for profile feedback
if (!bb->explicit_throw) {
return false;
@@ -511,13 +515,13 @@ static bool LayoutBlocks(struct BasicBlock* bb) {
if ((walker->block_type == kEntryBlock) || (Predecessors(walker) != 1)) {
break;
}
- BasicBlock* prev = walker->predecessors->Get(0);
+ BasicBlock* prev = GetBasicBlock(walker->predecessors->Get(0));
if (prev->conditional_branch) {
- if (prev->fall_through == walker) {
+ if (GetBasicBlock(prev->fall_through) == walker) {
// Already done - return
break;
}
- DCHECK_EQ(walker, prev->taken);
+ DCHECK_EQ(walker, GetBasicBlock(prev->taken));
// Got one. Flip it and exit
Instruction::Code opcode = prev->last_mir_insn->dalvikInsn.opcode;
switch (opcode) {
@@ -536,7 +540,7 @@ static bool LayoutBlocks(struct BasicBlock* bb) {
default: LOG(FATAL) << "Unexpected opcode " << opcode;
}
prev->last_mir_insn->dalvikInsn.opcode = opcode;
- BasicBlock* t_bb = prev->taken;
+ BasicBlockId t_bb = prev->taken;
prev->taken = prev->fall_through;
prev->fall_through = t_bb;
break;
@@ -556,8 +560,9 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) {
|| (bb->block_type == kExceptionHandling)
|| (bb->block_type == kExitBlock)
|| (bb->block_type == kDead)
- || ((bb->taken == NULL) || (bb->taken->block_type != kExceptionHandling))
- || (bb->successor_block_list.block_list_type != kNotUsed)
+ || (bb->taken == NullBasicBlockId)
+ || (GetBasicBlock(bb->taken)->block_type != kExceptionHandling)
+ || (bb->successor_block_list_type != kNotUsed)
|| (static_cast<int>(bb->last_mir_insn->dalvikInsn.opcode) != kMirOpCheck)) {
break;
}
@@ -578,19 +583,18 @@ bool MIRGraph::CombineBlocks(struct BasicBlock* bb) {
break;
}
// OK - got one. Combine
- BasicBlock* bb_next = bb->fall_through;
+ BasicBlock* bb_next = GetBasicBlock(bb->fall_through);
DCHECK(!bb_next->catch_entry);
DCHECK_EQ(Predecessors(bb_next), 1U);
- MIR* t_mir = bb->last_mir_insn->prev;
// Overwrite the kOpCheck insn with the paired opcode
DCHECK_EQ(bb_next->first_mir_insn, throw_insn);
*bb->last_mir_insn = *throw_insn;
- bb->last_mir_insn->prev = t_mir;
// Use the successor info from the next block
- bb->successor_block_list = bb_next->successor_block_list;
+ bb->successor_block_list_type = bb_next->successor_block_list_type;
+ bb->successor_blocks = bb_next->successor_blocks;
// Use the ending block linkage from the next block
bb->fall_through = bb_next->fall_through;
- bb->taken->block_type = kDead; // Kill the unused exception block
+ GetBasicBlock(bb->taken)->block_type = kDead; // Kill the unused exception block
bb->taken = bb_next->taken;
// Include the rest of the instructions
bb->last_mir_insn = bb_next->last_mir_insn;
@@ -625,49 +629,57 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) {
*/
if ((bb->block_type == kEntryBlock) | bb->catch_entry) {
temp_ssa_register_v_->ClearAllBits();
+ // Assume all ins are objects.
+ for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins;
+ in_reg < cu_->num_dalvik_registers; in_reg++) {
+ temp_ssa_register_v_->SetBit(in_reg);
+ }
if ((cu_->access_flags & kAccStatic) == 0) {
// If non-static method, mark "this" as non-null
int this_reg = cu_->num_dalvik_registers - cu_->num_ins;
- temp_ssa_register_v_->SetBit(this_reg);
+ temp_ssa_register_v_->ClearBit(this_reg);
}
} else if (bb->predecessors->Size() == 1) {
- BasicBlock* pred_bb = bb->predecessors->Get(0);
+ BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v);
if (pred_bb->block_type == kDalvikByteCode) {
// Check to see if predecessor had an explicit null-check.
MIR* last_insn = pred_bb->last_mir_insn;
Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
if (last_opcode == Instruction::IF_EQZ) {
- if (pred_bb->fall_through == bb) {
+ if (pred_bb->fall_through == bb->id) {
// The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that
// it can't be null.
- temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]);
+ temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]);
}
} else if (last_opcode == Instruction::IF_NEZ) {
- if (pred_bb->taken == bb) {
+ if (pred_bb->taken == bb->id) {
// The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be
// null.
- temp_ssa_register_v_->SetBit(last_insn->ssa_rep->uses[0]);
+ temp_ssa_register_v_->ClearBit(last_insn->ssa_rep->uses[0]);
}
}
}
} else {
- // Starting state is intersection of all incoming arcs
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
- BasicBlock* pred_bb = iter.Next();
+ // Starting state is union of all incoming arcs
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
DCHECK(pred_bb != NULL);
temp_ssa_register_v_->Copy(pred_bb->data_flow_info->ending_null_check_v);
while (true) {
- pred_bb = iter.Next();
+ pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) break;
if ((pred_bb->data_flow_info == NULL) ||
(pred_bb->data_flow_info->ending_null_check_v == NULL)) {
continue;
}
- temp_ssa_register_v_->Intersect(pred_bb->data_flow_info->ending_null_check_v);
+ temp_ssa_register_v_->Union(pred_bb->data_flow_info->ending_null_check_v);
}
}
+ // At this point, temp_ssa_register_v_ shows which sregs have an object definition with
+ // no intervening uses.
+
// Walk through the instruction in the block, updating as necessary
for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
if (mir->ssa_rep == NULL) {
@@ -675,11 +687,49 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) {
}
int df_attributes = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
- // Mark target of NEW* as non-null
- if (df_attributes & DF_NON_NULL_DST) {
+ // Might need a null check?
+ if (df_attributes & DF_HAS_NULL_CHKS) {
+ int src_idx;
+ if (df_attributes & DF_NULL_CHK_1) {
+ src_idx = 1;
+ } else if (df_attributes & DF_NULL_CHK_2) {
+ src_idx = 2;
+ } else {
+ src_idx = 0;
+ }
+ int src_sreg = mir->ssa_rep->uses[src_idx];
+ if (!temp_ssa_register_v_->IsBitSet(src_sreg)) {
+ // Eliminate the null check.
+ mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
+ } else {
+ // Do the null check.
+ mir->optimization_flags &= ~MIR_IGNORE_NULL_CHECK;
+ // Mark s_reg as null-checked
+ temp_ssa_register_v_->ClearBit(src_sreg);
+ }
+ }
+
+ if ((df_attributes & DF_A_WIDE) ||
+ (df_attributes & (DF_REF_A | DF_SETS_CONST | DF_NULL_TRANSFER)) == 0) {
+ continue;
+ }
+
+ /*
+ * First, mark all object definitions as requiring null check.
+ * Note: we can't tell if a CONST definition might be used as an object, so treat
+ * them all as object definitions.
+ */
+ if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) ||
+ (df_attributes & DF_SETS_CONST)) {
temp_ssa_register_v_->SetBit(mir->ssa_rep->defs[0]);
}
+ // Now, remove mark from all object definitions we know are non-null.
+ if (df_attributes & DF_NON_NULL_DST) {
+ // Mark target of NEW* as non-null
+ temp_ssa_register_v_->ClearBit(mir->ssa_rep->defs[0]);
+ }
+
// Mark non-null returns from invoke-style NEW*
if (df_attributes & DF_NON_NULL_RET) {
MIR* next_mir = mir->next;
@@ -687,13 +737,13 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) {
if (next_mir &&
next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
// Mark as null checked
- temp_ssa_register_v_->SetBit(next_mir->ssa_rep->defs[0]);
+ temp_ssa_register_v_->ClearBit(next_mir->ssa_rep->defs[0]);
} else {
if (next_mir) {
LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
- } else if (bb->fall_through) {
+ } else if (bb->fall_through != NullBasicBlockId) {
// Look in next basic block
- struct BasicBlock* next_bb = bb->fall_through;
+ struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through);
for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL;
tmir =tmir->next) {
if (static_cast<int>(tmir->dalvikInsn.opcode) >= static_cast<int>(kMirOpFirst)) {
@@ -702,7 +752,7 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) {
// First non-pseudo should be MOVE_RESULT_OBJECT
if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
// Mark as null checked
- temp_ssa_register_v_->SetBit(tmir->ssa_rep->defs[0]);
+ temp_ssa_register_v_->ClearBit(tmir->ssa_rep->defs[0]);
} else {
LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode;
}
@@ -715,40 +765,22 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) {
/*
* Propagate nullcheck state on register copies (including
* Phi pseudo copies. For the latter, nullcheck state is
- * the "and" of all the Phi's operands.
+ * the "or" of all the Phi's operands.
*/
if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) {
int tgt_sreg = mir->ssa_rep->defs[0];
int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 :
mir->ssa_rep->num_uses;
- bool null_checked = true;
+ bool needs_null_check = false;
for (int i = 0; i < operands; i++) {
- null_checked &= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]);
+ needs_null_check |= temp_ssa_register_v_->IsBitSet(mir->ssa_rep->uses[i]);
}
- if (null_checked) {
+ if (needs_null_check) {
temp_ssa_register_v_->SetBit(tgt_sreg);
- }
- }
-
- // Already nullchecked?
- if ((df_attributes & DF_HAS_NULL_CHKS) && !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
- int src_idx;
- if (df_attributes & DF_NULL_CHK_1) {
- src_idx = 1;
- } else if (df_attributes & DF_NULL_CHK_2) {
- src_idx = 2;
} else {
- src_idx = 0;
+ temp_ssa_register_v_->ClearBit(tgt_sreg);
}
- int src_sreg = mir->ssa_rep->uses[src_idx];
- if (temp_ssa_register_v_->IsBitSet(src_sreg)) {
- // Eliminate the null check
- mir->optimization_flags |= MIR_IGNORE_NULL_CHECK;
- } else {
- // Mark s_reg as null-checked
- temp_ssa_register_v_->SetBit(src_sreg);
- }
- }
+ }
}
// Did anything change?
@@ -762,11 +794,11 @@ bool MIRGraph::EliminateNullChecks(struct BasicBlock* bb) {
void MIRGraph::NullCheckElimination() {
if (!(cu_->disable_opt & (1 << kNullCheckElimination))) {
DCHECK(temp_ssa_register_v_ != NULL);
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
NullCheckEliminationInit(bb);
}
- PreOrderDfsIterator iter2(this, true /* iterative */);
+ RepeatingPreOrderDfsIterator iter2(this);
bool change = false;
for (BasicBlock* bb = iter2.Next(change); bb != NULL; bb = iter2.Next(change)) {
change = EliminateNullChecks(bb);
@@ -778,7 +810,7 @@ void MIRGraph::NullCheckElimination() {
}
void MIRGraph::BasicBlockCombine() {
- PreOrderDfsIterator iter(this, false /* not iterative */);
+ PreOrderDfsIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CombineBlocks(bb);
}
@@ -791,7 +823,7 @@ void MIRGraph::CodeLayout() {
if (cu_->enable_debug & (1 << kDebugVerifyDataflow)) {
VerifyDataflow();
}
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
LayoutBlocks(bb);
}
@@ -804,7 +836,7 @@ void MIRGraph::DumpCheckStats() {
Checkstats* stats =
static_cast<Checkstats*>(arena_->Alloc(sizeof(Checkstats), ArenaAllocator::kAllocDFInfo));
checkstats_ = stats;
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CountChecks(bb);
}
@@ -834,7 +866,7 @@ bool MIRGraph::BuildExtendedBBList(struct BasicBlock* bb) {
}
// Must be head of extended basic block.
BasicBlock* start_bb = bb;
- extended_basic_blocks_.push_back(bb);
+ extended_basic_blocks_.push_back(bb->id);
bool terminated_by_return = false;
// Visit blocks strictly dominated by this head.
while (bb != NULL) {
@@ -858,13 +890,13 @@ void MIRGraph::BasicBlockOptimization() {
if (!(cu_->disable_opt & (1 << kBBOpt))) {
DCHECK_EQ(cu_->num_compiler_temps, 0);
ClearAllVisitedFlags();
- PreOrderDfsIterator iter2(this, false /* not iterative */);
+ PreOrderDfsIterator iter2(this);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
BuildExtendedBBList(bb);
}
// Perform extended basic block optimizations.
for (unsigned int i = 0; i < extended_basic_blocks_.size(); i++) {
- BasicBlockOpt(extended_basic_blocks_[i]);
+ BasicBlockOpt(GetBasicBlock(extended_basic_blocks_[i]));
}
}
if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
index 90cec75039..963cbeb1d1 100644
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -30,10 +30,10 @@
#include "dex/compiler_internals.h"
#include "dex/dataflow_iterator-inl.h"
#include "dex/frontend.h"
-#include "mir_to_gbc.h"
-
#include "llvm/llvm_compilation_unit.h"
#include "llvm/utils_llvm.h"
+#include "mir_to_gbc.h"
+#include "thread-inl.h"
const char* kLabelFormat = "%c0x%x_%d";
const char kInvalidBlock = 0xff;
@@ -132,7 +132,7 @@ void MirConverter::ConvertPackedSwitch(BasicBlock* bb,
::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
::llvm::SwitchInst* sw =
- irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id),
+ irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
payload->case_count);
for (uint16_t i = 0; i < payload->case_count; ++i) {
@@ -143,8 +143,8 @@ void MirConverter::ConvertPackedSwitch(BasicBlock* bb,
::llvm::MDNode* switch_node =
::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
sw->setMetadata("SwitchTable", switch_node);
- bb->taken = NULL;
- bb->fall_through = NULL;
+ bb->taken = NullBasicBlockId;
+ bb->fall_through = NullBasicBlockId;
}
void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
@@ -159,7 +159,7 @@ void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
::llvm::Value* value = GetLLVMValue(rl_src.orig_sreg);
::llvm::SwitchInst* sw =
- irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through->id),
+ irb_->CreateSwitch(value, GetLLVMBlock(bb->fall_through),
payload->case_count);
for (size_t i = 0; i < payload->case_count; ++i) {
@@ -170,8 +170,8 @@ void MirConverter::ConvertSparseSwitch(BasicBlock* bb,
::llvm::MDNode* switch_node =
::llvm::MDNode::get(*context_, irb_->getInt32(table_offset));
sw->setMetadata("SwitchTable", switch_node);
- bb->taken = NULL;
- bb->fall_through = NULL;
+ bb->taken = NullBasicBlockId;
+ bb->fall_through = NullBasicBlockId;
}
void MirConverter::ConvertSget(int32_t field_index,
@@ -311,22 +311,22 @@ void MirConverter::EmitSuspendCheck() {
void MirConverter::ConvertCompareAndBranch(BasicBlock* bb, MIR* mir,
ConditionCode cc, RegLocation rl_src1, RegLocation rl_src2) {
- if (bb->taken->start_offset <= mir->offset) {
+ if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
EmitSuspendCheck();
}
::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
::llvm::Value* src2 = GetLLVMValue(rl_src2.orig_sreg);
::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
cond_value->setName(StringPrintf("t%d", temp_name_++));
- irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id),
- GetLLVMBlock(bb->fall_through->id));
+ irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
+ GetLLVMBlock(bb->fall_through));
// Don't redo the fallthrough branch in the BB driver
- bb->fall_through = NULL;
+ bb->fall_through = NullBasicBlockId;
}
void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb,
MIR* mir, ConditionCode cc, RegLocation rl_src1) {
- if (bb->taken->start_offset <= mir->offset) {
+ if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= mir->offset) {
EmitSuspendCheck();
}
::llvm::Value* src1 = GetLLVMValue(rl_src1.orig_sreg);
@@ -337,10 +337,10 @@ void MirConverter::ConvertCompareZeroAndBranch(BasicBlock* bb,
src2 = irb_->getInt32(0);
}
::llvm::Value* cond_value = ConvertCompare(cc, src1, src2);
- irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken->id),
- GetLLVMBlock(bb->fall_through->id));
+ irb_->CreateCondBr(cond_value, GetLLVMBlock(bb->taken),
+ GetLLVMBlock(bb->fall_through));
// Don't redo the fallthrough branch in the BB driver
- bb->fall_through = NULL;
+ bb->fall_through = NullBasicBlockId;
}
::llvm::Value* MirConverter::GenDivModOp(bool is_div, bool is_long,
@@ -941,10 +941,10 @@ bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb,
case Instruction::GOTO:
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
- if (bb->taken->start_offset <= bb->start_offset) {
+ if (mir_graph_->GetBasicBlock(bb->taken)->start_offset <= bb->start_offset) {
EmitSuspendCheck();
}
- irb_->CreateBr(GetLLVMBlock(bb->taken->id));
+ irb_->CreateBr(GetLLVMBlock(bb->taken));
}
break;
@@ -1190,11 +1190,11 @@ bool MirConverter::ConvertMIRNode(MIR* mir, BasicBlock* bb,
* If it might rethrow, force termination
* of the following block.
*/
- if (bb->fall_through == NULL) {
+ if (bb->fall_through == NullBasicBlockId) {
irb_->CreateUnreachable();
} else {
- bb->fall_through->fall_through = NULL;
- bb->fall_through->taken = NULL;
+ mir_graph_->GetBasicBlock(bb->fall_through)->fall_through = NullBasicBlockId;
+ mir_graph_->GetBasicBlock(bb->fall_through)->taken = NullBasicBlockId;
}
break;
@@ -1552,7 +1552,7 @@ void MirConverter::HandlePhiNodes(BasicBlock* bb, ::llvm::BasicBlock* llvm_bb) {
if (rl_dest.high_word) {
continue; // No Phi node - handled via low word
}
- int* incoming = reinterpret_cast<int*>(mir->dalvikInsn.vB);
+ BasicBlockId* incoming = mir->meta.phi_incoming;
::llvm::Type* phi_type =
LlvmTypeFromLocRec(rl_dest);
::llvm::PHINode* phi = irb_->CreatePHI(phi_type, mir->ssa_rep->num_uses);
@@ -1597,8 +1597,8 @@ void MirConverter::ConvertExtendedMIR(BasicBlock* bb, MIR* mir,
break;
}
case kMirOpNop:
- if ((mir == bb->last_mir_insn) && (bb->taken == NULL) &&
- (bb->fall_through == NULL)) {
+ if ((mir == bb->last_mir_insn) && (bb->taken == NullBasicBlockId) &&
+ (bb->fall_through == NullBasicBlockId)) {
irb_->CreateUnreachable();
}
break;
@@ -1718,25 +1718,23 @@ bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) {
SSARepresentation* ssa_rep = work_half->ssa_rep;
work_half->ssa_rep = mir->ssa_rep;
mir->ssa_rep = ssa_rep;
- work_half->meta.original_opcode = work_half->dalvikInsn.opcode;
work_half->dalvikInsn.opcode = static_cast<Instruction::Code>(kMirOpNop);
- if (bb->successor_block_list.block_list_type == kCatch) {
+ if (bb->successor_block_list_type == kCatch) {
::llvm::Function* intr = intrinsic_helper_->GetIntrinsicFunction(
art::llvm::IntrinsicHelper::CatchTargets);
::llvm::Value* switch_key =
irb_->CreateCall(intr, irb_->getInt32(mir->offset));
- GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_block_list.blocks);
+ GrowableArray<SuccessorBlockInfo*>::Iterator iter(bb->successor_blocks);
// New basic block to use for work half
::llvm::BasicBlock* work_bb =
::llvm::BasicBlock::Create(*context_, "", func_);
::llvm::SwitchInst* sw =
- irb_->CreateSwitch(switch_key, work_bb,
- bb->successor_block_list.blocks->Size());
+ irb_->CreateSwitch(switch_key, work_bb, bb->successor_blocks->Size());
while (true) {
SuccessorBlockInfo *successor_block_info = iter.Next();
if (successor_block_info == NULL) break;
::llvm::BasicBlock *target =
- GetLLVMBlock(successor_block_info->block->id);
+ GetLLVMBlock(successor_block_info->block);
int type_index = successor_block_info->key;
sw->addCase(irb_->getInt32(type_index), target);
}
@@ -1761,9 +1759,9 @@ bool MirConverter::BlockBitcodeConversion(BasicBlock* bb) {
}
if (bb->block_type == kEntryBlock) {
- entry_target_bb_ = GetLLVMBlock(bb->fall_through->id);
- } else if ((bb->fall_through != NULL) && !bb->terminated_by_return) {
- irb_->CreateBr(GetLLVMBlock(bb->fall_through->id));
+ entry_target_bb_ = GetLLVMBlock(bb->fall_through);
+ } else if ((bb->fall_through != NullBasicBlockId) && !bb->terminated_by_return) {
+ irb_->CreateBr(GetLLVMBlock(bb->fall_through));
}
return false;
@@ -1877,7 +1875,7 @@ void MirConverter::MethodMIR2Bitcode() {
CreateFunction();
// Create an LLVM basic block for each MIR block in dfs preorder
- PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
+ PreOrderDfsIterator iter(mir_graph_);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
CreateLLVMBasicBlock(bb);
}
@@ -1909,7 +1907,7 @@ void MirConverter::MethodMIR2Bitcode() {
}
}
- PreOrderDfsIterator iter2(mir_graph_, false /* not iterative */);
+ PreOrderDfsIterator iter2(mir_graph_);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
BlockBitcodeConversion(bb);
}
@@ -1972,7 +1970,7 @@ void MirConverter::MethodMIR2Bitcode() {
::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
new ::llvm::tool_output_file(fname.c_str(), errmsg,
- ::llvm::sys::fs::F_Binary));
+ ::llvm::raw_fd_ostream::F_Binary));
if (!errmsg.empty()) {
LOG(ERROR) << "Failed to create bitcode output file: " << errmsg;
diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h
index 2f54190ae7..ffaaf84503 100644
--- a/compiler/dex/quick/arm/arm_lir.h
+++ b/compiler/dex/quick/arm/arm_lir.h
@@ -296,6 +296,8 @@ enum ArmOpcode {
kThumbOrr, // orr [0100001100] rm[5..3] rd[2..0].
kThumbPop, // pop [1011110] r[8..8] rl[7..0].
kThumbPush, // push [1011010] r[8..8] rl[7..0].
+ kThumbRev, // rev [1011101000] rm[5..3] rd[2..0]
+ kThumbRevsh, // revsh [1011101011] rm[5..3] rd[2..0]
kThumbRorRR, // ror [0100000111] rs[5..3] rd[2..0].
kThumbSbc, // sbc [0100000110] rm[5..3] rd[2..0].
kThumbStmia, // stmia [11000] rn[10..8] reglist [7.. 0].
@@ -378,6 +380,8 @@ enum ArmOpcode {
kThumb2CmnRR, // cmn [111010110001] rn[19..16] [0000] [1111] [0000] rm[3..0].
kThumb2EorRRR, // eor [111010101000] rn[19..16] [0000] rd[11..8] [0000] rm[3..0].
kThumb2MulRRR, // mul [111110110000] rn[19..16] [1111] rd[11..8] [0000] rm[3..0].
+ kThumb2SdivRRR, // sdiv [111110111001] rn[19..16] [1111] rd[11..8] [1111] rm[3..0].
+ kThumb2UdivRRR, // udiv [111110111011] rn[19..16] [1111] rd[11..8] [1111] rm[3..0].
kThumb2MnvRR, // mvn [11101010011011110] rd[11-8] [0000] rm[3..0].
kThumb2RsubRRI8, // rsub [111100011100] rn[19..16] [0000] rd[11..8] imm8[7..0].
kThumb2NegRR, // actually rsub rd, rn, #0.
@@ -399,6 +403,8 @@ enum ArmOpcode {
kThumb2AdcRRI8, // adc [111100010101] rn[19..16] [0] imm3 rd[11..8] imm8.
kThumb2SubRRI8, // sub [111100011011] rn[19..16] [0] imm3 rd[11..8] imm8.
kThumb2SbcRRI8, // sbc [111100010111] rn[19..16] [0] imm3 rd[11..8] imm8.
+ kThumb2RevRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1000 rm[3..0]
+ kThumb2RevshRR, // rev [111110101001] rm[19..16] [1111] rd[11..8] 1011 rm[3..0]
kThumb2It, // it [10111111] firstcond[7-4] mask[3-0].
kThumb2Fmstat, // fmstat [11101110111100011111101000010000].
kThumb2Vcmpd, // vcmp [111011101] D [11011] rd[15-12] [1011] E [1] M [0] rm[3-0].
@@ -462,7 +468,7 @@ enum ArmOpDmbOptions {
// Instruction assembly field_loc kind.
enum ArmEncodingKind {
- kFmtUnused,
+ kFmtUnused, // Unused field and marks end of formats.
kFmtBitBlt, // Bit string using end/start.
kFmtDfp, // Double FP reg.
kFmtSfp, // Single FP reg.
@@ -477,6 +483,7 @@ enum ArmEncodingKind {
kFmtBrOffset, // Signed extended [26,11,13,21-16,10-0]:0.
kFmtFPImm, // Encoded floating point immediate.
kFmtOff24, // 24-bit Thumb2 unconditional branch encoding.
+ kFmtSkip, // Unused field, but continue to next.
};
// Struct used to define the snippet positions for each Thumb opcode.
@@ -492,6 +499,7 @@ struct ArmEncodingMap {
const char* name;
const char* fmt;
int size; // Note: size is in bytes.
+ FixupKind fixup;
};
} // namespace art
diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc
index 0649c9f319..3d0f263fad 100644
--- a/compiler/dex/quick/arm/assemble_arm.cc
+++ b/compiler/dex/quick/arm/assemble_arm.cc
@@ -37,9 +37,9 @@ namespace art {
* fmt: for pretty-printing
*/
#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \
- k3, k3s, k3e, flags, name, fmt, size) \
+ k3, k3s, k3e, flags, name, fmt, size, fixup) \
{skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \
- {k3, k3s, k3e}}, opcode, flags, name, fmt, size}
+ {k3, k3s, k3e}}, opcode, flags, name, fmt, size, fixup}
/* Instruction dump string format keys: !pf, where "!" is the start
* of the key, "p" is which numeric operand to use and "f" is the
@@ -79,916 +79,966 @@ namespace art {
const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = {
ENCODING_MAP(kArm16BitData, 0x0000,
kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
- kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2),
+ kFmtUnused, -1, -1, IS_UNARY_OP, "data", "0x!0h(!0d)", 2, kFixupNone),
ENCODING_MAP(kThumbAdcRR, 0x4140,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES | USES_CCODES,
- "adcs", "!0C, !1C", 2),
+ "adcs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRI3, 0x1c00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "adds", "!0C, !1C, #!2d", 2),
+ "adds", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbAddRI8, 0x3000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
- "adds", "!0C, !0C, #!1d", 2),
+ "adds", "!0C, !0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRR, 0x1800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
- "adds", "!0C, !1C, !2C", 2),
+ "adds", "!0C, !1C, !2C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRLH, 0x4440,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
- "add", "!0C, !1C", 2),
+ "add", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRHL, 0x4480,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
- "add", "!0C, !1C", 2),
+ "add", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddRRHH, 0x44c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE01,
- "add", "!0C, !1C", 2),
+ "add", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAddPcRel, 0xa000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | IS_BRANCH | NEEDS_FIXUP,
- "add", "!0C, pc, #!1E", 2),
+ "add", "!0C, pc, #!1E", 2, kFixupLoad),
ENCODING_MAP(kThumbAddSpRel, 0xa800,
- kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF_SP | REG_USE_SP,
- "add", "!0C, sp, #!2E", 2),
+ "add", "!0C, sp, #!2E", 2, kFixupNone),
ENCODING_MAP(kThumbAddSpI7, 0xb000,
kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
- "add", "sp, #!0d*4", 2),
+ "add", "sp, #!0d*4", 2, kFixupNone),
ENCODING_MAP(kThumbAndRR, 0x4000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "ands", "!0C, !1C", 2),
+ "ands", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbAsrRRI5, 0x1000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "asrs", "!0C, !1C, #!2d", 2),
+ "asrs", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbAsrRR, 0x4100,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "asrs", "!0C, !1C", 2),
+ "asrs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbBCond, 0xd000,
kFmtBitBlt, 7, 0, kFmtBitBlt, 11, 8, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | USES_CCODES |
- NEEDS_FIXUP, "b!1c", "!0t", 2),
+ NEEDS_FIXUP, "b!1c", "!0t", 2, kFixupCondBranch),
ENCODING_MAP(kThumbBUncond, 0xe000,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP,
- "b", "!0t", 2),
+ "b", "!0t", 2, kFixupT1Branch),
ENCODING_MAP(kThumbBicRR, 0x4380,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "bics", "!0C, !1C", 2),
+ "bics", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbBkpt, 0xbe00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
- "bkpt", "!0d", 2),
+ "bkpt", "!0d", 2, kFixupNone),
ENCODING_MAP(kThumbBlx1, 0xf000,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
- NEEDS_FIXUP, "blx_1", "!0u", 2),
+ NEEDS_FIXUP, "blx_1", "!0u", 2, kFixupBlx1),
ENCODING_MAP(kThumbBlx2, 0xe800,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF_LR |
- NEEDS_FIXUP, "blx_2", "!0v", 2),
+ NEEDS_FIXUP, "blx_2", "!0v", 2, kFixupLabel),
ENCODING_MAP(kThumbBl1, 0xf000,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
- kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
- "bl_1", "!0u", 2),
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+ "bl_1", "!0u", 2, kFixupBl1),
ENCODING_MAP(kThumbBl2, 0xf800,
kFmtBitBlt, 10, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
- kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR,
- "bl_2", "!0v", 2),
+ kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | NEEDS_FIXUP,
+ "bl_2", "!0v", 2, kFixupLabel),
ENCODING_MAP(kThumbBlxR, 0x4780,
kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_USE0 | IS_BRANCH | REG_DEF_LR,
- "blx", "!0C", 2),
+ "blx", "!0C", 2, kFixupNone),
ENCODING_MAP(kThumbBx, 0x4700,
kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
- "bx", "!0C", 2),
+ "bx", "!0C", 2, kFixupNone),
ENCODING_MAP(kThumbCmnRR, 0x42c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmn", "!0C, !1C", 2),
+ "cmn", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpRI8, 0x2800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | SETS_CCODES,
- "cmp", "!0C, #!1d", 2),
+ "cmp", "!0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbCmpRR, 0x4280,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpLH, 0x4540,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpHL, 0x4580,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbCmpHH, 0x45c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 2),
+ "cmp", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbEorRR, 0x4040,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "eors", "!0C, !1C", 2),
+ "eors", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbLdmia, 0xc800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
- "ldmia", "!0C!!, <!1R>", 2),
+ "ldmia", "!0C!!, <!1R>", 2, kFixupNone),
ENCODING_MAP(kThumbLdrRRI5, 0x6800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldr", "!0C, [!1C, #!2E]", 2),
+ "ldr", "!0C, [!1C, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrRRR, 0x5800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldr", "!0C, [!1C, !2C]", 2),
+ "ldr", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrPcRel, 0x4800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC
- | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2),
+ | IS_LOAD | NEEDS_FIXUP, "ldr", "!0C, [pc, #!1E]", 2, kFixupLoad),
ENCODING_MAP(kThumbLdrSpRel, 0x9800,
- kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0 | REG_USE_SP
- | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2),
+ | IS_LOAD, "ldr", "!0C, [sp, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrbRRI5, 0x7800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrb", "!0C, [!1C, #2d]", 2),
+ "ldrb", "!0C, [!1C, #2d]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrbRRR, 0x5c00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrb", "!0C, [!1C, !2C]", 2),
+ "ldrb", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrhRRI5, 0x8800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrh", "!0C, [!1C, #!2F]", 2),
+ "ldrh", "!0C, [!1C, #!2F]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrhRRR, 0x5a00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrh", "!0C, [!1C, !2C]", 2),
+ "ldrh", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrsbRRR, 0x5600,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsb", "!0C, [!1C, !2C]", 2),
+ "ldrsb", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLdrshRRR, 0x5e00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsh", "!0C, [!1C, !2C]", 2),
+ "ldrsh", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbLslRRI5, 0x0000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "lsls", "!0C, !1C, #!2d", 2),
+ "lsls", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbLslRR, 0x4080,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "lsls", "!0C, !1C", 2),
+ "lsls", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbLsrRRI5, 0x0800,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "lsrs", "!0C, !1C, #!2d", 2),
+ "lsrs", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbLsrRR, 0x40c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "lsrs", "!0C, !1C", 2),
+ "lsrs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovImm, 0x2000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | SETS_CCODES,
- "movs", "!0C, #!1d", 2),
+ "movs", "!0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR, 0x1c00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "movs", "!0C, !1C", 2),
+ "movs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR_H2H, 0x46c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 2),
+ "mov", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR_H2L, 0x4640,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 2),
+ "mov", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMovRR_L2H, 0x4680,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 2),
+ "mov", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMul, 0x4340,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "muls", "!0C, !1C", 2),
+ "muls", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbMvn, 0x43c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "mvns", "!0C, !1C", 2),
+ "mvns", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbNeg, 0x4240,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "negs", "!0C, !1C", 2),
+ "negs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbOrr, 0x4300,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "orrs", "!0C, !1C", 2),
+ "orrs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbPop, 0xbc00,
kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
- | IS_LOAD, "pop", "<!0R>", 2),
+ | IS_LOAD, "pop", "<!0R>", 2, kFixupNone),
ENCODING_MAP(kThumbPush, 0xb400,
kFmtBitBlt, 8, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
- | IS_STORE, "push", "<!0R>", 2),
+ | IS_STORE, "push", "<!0R>", 2, kFixupNone),
+ ENCODING_MAP(kThumbRev, 0xba00,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1,
+ "rev", "!0C, !1C", 2, kFixupNone),
+ ENCODING_MAP(kThumbRevsh, 0xbac0,
+ kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0_USE1,
+ "rev", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbRorRR, 0x41c0,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | SETS_CCODES,
- "rors", "!0C, !1C", 2),
+ "rors", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbSbc, 0x4180,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE01 | USES_CCODES | SETS_CCODES,
- "sbcs", "!0C, !1C", 2),
+ "sbcs", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumbStmia, 0xc000,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | REG_USE0 | REG_USE_LIST1 | IS_STORE,
- "stmia", "!0C!!, <!1R>", 2),
+ "stmia", "!0C!!, <!1R>", 2, kFixupNone),
ENCODING_MAP(kThumbStrRRI5, 0x6000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "str", "!0C, [!1C, #!2E]", 2),
+ "str", "!0C, [!1C, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbStrRRR, 0x5000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
- "str", "!0C, [!1C, !2C]", 2),
+ "str", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbStrSpRel, 0x9000,
- kFmtBitBlt, 10, 8, kFmtUnused, -1, -1, kFmtBitBlt, 7, 0,
+ kFmtBitBlt, 10, 8, kFmtSkip, -1, -1, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE0 | REG_USE_SP
- | IS_STORE, "str", "!0C, [sp, #!2E]", 2),
+ | IS_STORE, "str", "!0C, [sp, #!2E]", 2, kFixupNone),
ENCODING_MAP(kThumbStrbRRI5, 0x7000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strb", "!0C, [!1C, #!2d]", 2),
+ "strb", "!0C, [!1C, #!2d]", 2, kFixupNone),
ENCODING_MAP(kThumbStrbRRR, 0x5400,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
- "strb", "!0C, [!1C, !2C]", 2),
+ "strb", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbStrhRRI5, 0x8000,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 10, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strh", "!0C, [!1C, #!2F]", 2),
+ "strh", "!0C, [!1C, #!2F]", 2, kFixupNone),
ENCODING_MAP(kThumbStrhRRR, 0x5200,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE012 | IS_STORE,
- "strh", "!0C, [!1C, !2C]", 2),
+ "strh", "!0C, [!1C, !2C]", 2, kFixupNone),
ENCODING_MAP(kThumbSubRRI3, 0x1e00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "subs", "!0C, !1C, #!2d", 2),
+ "subs", "!0C, !1C, #!2d", 2, kFixupNone),
ENCODING_MAP(kThumbSubRI8, 0x3800,
kFmtBitBlt, 10, 8, kFmtBitBlt, 7, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES,
- "subs", "!0C, #!1d", 2),
+ "subs", "!0C, #!1d", 2, kFixupNone),
ENCODING_MAP(kThumbSubRRR, 0x1a00,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtBitBlt, 8, 6,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12 | SETS_CCODES,
- "subs", "!0C, !1C, !2C", 2),
+ "subs", "!0C, !1C, !2C", 2, kFixupNone),
ENCODING_MAP(kThumbSubSpI7, 0xb080,
kFmtBitBlt, 6, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP,
- "sub", "sp, #!0d*4", 2),
+ "sub", "sp, #!0d*4", 2, kFixupNone),
ENCODING_MAP(kThumbSwi, 0xdf00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH,
- "swi", "!0d", 2),
+ "swi", "!0d", 2, kFixupNone),
ENCODING_MAP(kThumbTst, 0x4200,
kFmtBitBlt, 2, 0, kFmtBitBlt, 5, 3, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP | REG_USE01 | SETS_CCODES,
- "tst", "!0C, !1C", 2),
+ "tst", "!0C, !1C", 2, kFixupNone),
ENCODING_MAP(kThumb2Vldrs, 0xed900a00,
kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
- REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4),
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0s, [!1C, #!2E]", 4, kFixupVLoad),
ENCODING_MAP(kThumb2Vldrd, 0xed900b00,
kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD |
- REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4),
+ REG_DEF_LR | NEEDS_FIXUP, "vldr", "!0S, [!1C, #!2E]", 4, kFixupVLoad),
ENCODING_MAP(kThumb2Vmuls, 0xee200a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12,
- "vmuls", "!0s, !1s, !2s", 4),
+ "vmuls", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmuld, 0xee200b00,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vmuld", "!0S, !1S, !2S", 4),
+ "vmuld", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vstrs, 0xed800a00,
kFmtSfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "vstr", "!0s, [!1C, #!2E]", 4),
+ "vstr", "!0s, [!1C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Vstrd, 0xed800b00,
kFmtDfp, 22, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "vstr", "!0S, [!1C, #!2E]", 4),
+ "vstr", "!0S, [!1C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsubs, 0xee300a40,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vsub", "!0s, !1s, !2s", 4),
+ "vsub", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsubd, 0xee300b40,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vsub", "!0S, !1S, !2S", 4),
+ "vsub", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vadds, 0xee300a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vadd", "!0s, !1s, !2s", 4),
+ "vadd", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vaddd, 0xee300b00,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vadd", "!0S, !1S, !2S", 4),
+ "vadd", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vdivs, 0xee800a00,
kFmtSfp, 22, 12, kFmtSfp, 7, 16, kFmtSfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vdivs", "!0s, !1s, !2s", 4),
+ "vdivs", "!0s, !1s, !2s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vdivd, 0xee800b00,
kFmtDfp, 22, 12, kFmtDfp, 7, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "vdivd", "!0S, !1S, !2S", 4),
+ "vdivd", "!0S, !1S, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtIF, 0xeeb80ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f32", "!0s, !1s", 4),
+ "vcvt.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtID, 0xeeb80bc0,
kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f64", "!0S, !1s", 4),
+ "vcvt.f64", "!0S, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtFI, 0xeebd0ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.s32.f32 ", "!0s, !1s", 4),
+ "vcvt.s32.f32 ", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtDI, 0xeebd0bc0,
kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.s32.f64 ", "!0s, !1S", 4),
+ "vcvt.s32.f64 ", "!0s, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtFd, 0xeeb70ac0,
kFmtDfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f64.f32 ", "!0S, !1s", 4),
+ "vcvt.f64.f32 ", "!0S, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2VcvtDF, 0xeeb70bc0,
kFmtSfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vcvt.f32.f64 ", "!0s, !1S", 4),
+ "vcvt.f32.f64 ", "!0s, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsqrts, 0xeeb10ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vsqrt.f32 ", "!0s, !1s", 4),
+ "vsqrt.f32 ", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vsqrtd, 0xeeb10bc0,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vsqrt.f64 ", "!0S, !1S", 4),
+ "vsqrt.f64 ", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2MovImmShift, 0xf04f0000, /* no setflags encoding */
kFmtBitBlt, 11, 8, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "mov", "!0C, #!1m", 4),
+ "mov", "!0C, #!1m", 4, kFixupNone),
ENCODING_MAP(kThumb2MovImm16, 0xf2400000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "mov", "!0C, #!1M", 4),
+ "mov", "!0C, #!1M", 4, kFixupNone),
ENCODING_MAP(kThumb2StrRRI12, 0xf8c00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "str", "!0C, [!1C, #!2d]", 4),
+ "str", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrRRI12, 0xf8d00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldr", "!0C, [!1C, #!2d]", 4),
+ "ldr", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrRRI8Predec, 0xf8400c00,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "str", "!0C, [!1C, #-!2d]", 4),
+ "str", "!0C, [!1C, #-!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrRRI8Predec, 0xf8500c00,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 8, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldr", "!0C, [!1C, #-!2d]", 4),
+ "ldr", "!0C, [!1C, #-!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Cbnz, 0xb900, /* Note: does not affect flags */
kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
- NEEDS_FIXUP, "cbnz", "!0C,!1t", 2),
+ NEEDS_FIXUP, "cbnz", "!0C,!1t", 2, kFixupCBxZ),
ENCODING_MAP(kThumb2Cbz, 0xb100, /* Note: does not affect flags */
kFmtBitBlt, 2, 0, kFmtImm6, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | IS_BRANCH |
- NEEDS_FIXUP, "cbz", "!0C,!1t", 2),
+ NEEDS_FIXUP, "cbz", "!0C,!1t", 2, kFixupCBxZ),
ENCODING_MAP(kThumb2AddRRI12, 0xf2000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
- "add", "!0C,!1C,#!2d", 4),
+ "add", "!0C,!1C,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2MovRR, 0xea4f0000, /* no setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "mov", "!0C, !1C", 4),
+ "mov", "!0C, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovs, 0xeeb00a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f32 ", " !0s, !1s", 4),
+ "vmov.f32 ", " !0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovd, 0xeeb00b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f64 ", " !0S, !1S", 4),
+ "vmov.f64 ", " !0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldmia, 0xe8900000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
- "ldmia", "!0C!!, <!1R>", 4),
+ "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2Stmia, 0xe8800000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_USE_LIST1 | IS_STORE,
- "stmia", "!0C!!, <!1R>", 4),
+ "stmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2AddRRR, 0xeb100000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "adds", "!0C, !1C, !2C!3H", 4),
+ "adds", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2SubRRR, 0xebb00000, /* setflags enconding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "subs", "!0C, !1C, !2C!3H", 4),
+ "subs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2SbcRRR, 0xeb700000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | USES_CCODES | SETS_CCODES,
- "sbcs", "!0C, !1C, !2C!3H", 4),
+ "sbcs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2CmpRR, 0xebb00f00,
kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
- "cmp", "!0C, !1C", 4),
+ "cmp", "!0C, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2SubRRI12, 0xf2a00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1,/* Note: doesn't affect flags */
- "sub", "!0C,!1C,#!2d", 4),
+ "sub", "!0C,!1C,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2MvnImm12, 0xf06f0000, /* no setflags encoding */
kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "mvn", "!0C, #!1n", 4),
+ "mvn", "!0C, #!1n", 4, kFixupNone),
ENCODING_MAP(kThumb2Sel, 0xfaa0f080,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE12 | USES_CCODES,
- "sel", "!0C, !1C, !2C", 4),
+ "sel", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2Ubfx, 0xf3c00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
- "ubfx", "!0C, !1C, #!2d, #!3d", 4),
+ "ubfx", "!0C, !1C, #!2d, #!3d", 4, kFixupNone),
ENCODING_MAP(kThumb2Sbfx, 0xf3400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtLsb, -1, -1,
kFmtBWidth, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
- "sbfx", "!0C, !1C, #!2d, #!3d", 4),
+ "sbfx", "!0C, !1C, #!2d, #!3d", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrRRR, 0xf8500000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldr", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrhRRR, 0xf8300000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrshRRR, 0xf9300000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrsh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrbRRR, 0xf8100000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrsbRRR, 0xf9100000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_DEF0_USE12 | IS_LOAD,
- "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "ldrsb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrRRR, 0xf8400000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
- "str", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "str", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrhRRR, 0xf8200000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
- "strh", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "strh", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrbRRR, 0xf8000000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 5, 4, IS_QUAD_OP | REG_USE012 | IS_STORE,
- "strb", "!0C, [!1C, !2C, LSL #!3d]", 4),
+ "strb", "!0C, [!1C, !2C, LSL #!3d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrhRRI12, 0xf8b00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrh", "!0C, [!1C, #!2d]", 4),
+ "ldrh", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrshRRI12, 0xf9b00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrsh", "!0C, [!1C, #!2d]", 4),
+ "ldrsh", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrbRRI12, 0xf8900000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrb", "!0C, [!1C, #!2d]", 4),
+ "ldrb", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrsbRRI12, 0xf9900000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrsb", "!0C, [!1C, #!2d]", 4),
+ "ldrsb", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrhRRI12, 0xf8a00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strh", "!0C, [!1C, #!2d]", 4),
+ "strh", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrbRRI12, 0xf8800000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 11, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE01 | IS_STORE,
- "strb", "!0C, [!1C, #!2d]", 4),
+ "strb", "!0C, [!1C, #!2d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Pop, 0xe8bd0000,
kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0
- | IS_LOAD | NEEDS_FIXUP, "pop", "<!0R>", 4),
+ | IS_LOAD | NEEDS_FIXUP, "pop", "<!0R>", 4, kFixupPushPop),
ENCODING_MAP(kThumb2Push, 0xe92d0000,
kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_LIST0
- | IS_STORE | NEEDS_FIXUP, "push", "<!0R>", 4),
+ | IS_STORE | NEEDS_FIXUP, "push", "<!0R>", 4, kFixupPushPop),
ENCODING_MAP(kThumb2CmpRI12, 0xf1b00f00,
kFmtBitBlt, 19, 16, kFmtModImm, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | SETS_CCODES,
- "cmp", "!0C, #!1m", 4),
+ "cmp", "!0C, #!1m", 4, kFixupNone),
ENCODING_MAP(kThumb2AdcRRR, 0xeb500000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "adcs", "!0C, !1C, !2C!3H", 4),
+ "adcs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2AndRRR, 0xea000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "and", "!0C, !1C, !2C!3H", 4),
+ "and", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2BicRRR, 0xea200000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "bic", "!0C, !1C, !2C!3H", 4),
+ "bic", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2CmnRR, 0xeb000000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "cmn", "!0C, !1C, shift !2d", 4),
+ "cmn", "!0C, !1C, shift !2d", 4, kFixupNone),
ENCODING_MAP(kThumb2EorRRR, 0xea800000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "eor", "!0C, !1C, !2C!3H", 4),
+ "eor", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2MulRRR, 0xfb00f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "mul", "!0C, !1C, !2C", 4),
+ "mul", "!0C, !1C, !2C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2SdivRRR, 0xfb90f0f0,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "sdiv", "!0C, !1C, !2C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2UdivRRR, 0xfbb0f0f0,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
+ "udiv", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2MnvRR, 0xea6f0000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "mvn", "!0C, !1C, shift !2d", 4),
+ "mvn", "!0C, !1C, shift !2d", 4, kFixupNone),
ENCODING_MAP(kThumb2RsubRRI8, 0xf1d00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "rsb", "!0C,!1C,#!2m", 4),
+ "rsb", "!0C,!1C,#!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2NegRR, 0xf1d00000, /* instance of rsub */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "neg", "!0C,!1C", 4),
+ "neg", "!0C,!1C", 4, kFixupNone),
ENCODING_MAP(kThumb2OrrRRR, 0xea400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12,
- "orr", "!0C, !1C, !2C!3H", 4),
+ "orr", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2TstRR, 0xea100f00,
kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, kFmtShift, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE01 | SETS_CCODES,
- "tst", "!0C, !1C, shift !2d", 4),
+ "tst", "!0C, !1C, shift !2d", 4, kFixupNone),
ENCODING_MAP(kThumb2LslRRR, 0xfa00f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "lsl", "!0C, !1C, !2C", 4),
+ "lsl", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2LsrRRR, 0xfa20f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "lsr", "!0C, !1C, !2C", 4),
+ "lsr", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2AsrRRR, 0xfa40f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "asr", "!0C, !1C, !2C", 4),
+ "asr", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2RorRRR, 0xfa60f000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "ror", "!0C, !1C, !2C", 4),
+ "ror", "!0C, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2LslRRI5, 0xea4f0000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "lsl", "!0C, !1C, #!2d", 4),
+ "lsl", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2LsrRRI5, 0xea4f0010,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "lsr", "!0C, !1C, #!2d", 4),
+ "lsr", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2AsrRRI5, 0xea4f0020,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "asr", "!0C, !1C, #!2d", 4),
+ "asr", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2RorRRI5, 0xea4f0030,
kFmtBitBlt, 11, 8, kFmtBitBlt, 3, 0, kFmtShift5, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "ror", "!0C, !1C, #!2d", 4),
+ "ror", "!0C, !1C, #!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2BicRRI8, 0xf0200000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "bic", "!0C, !1C, #!2m", 4),
+ "bic", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2AndRRI8, 0xf0000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "and", "!0C, !1C, #!2m", 4),
+ "and", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2OrrRRI8, 0xf0400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "orr", "!0C, !1C, #!2m", 4),
+ "orr", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2EorRRI8, 0xf0800000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1,
- "eor", "!0C, !1C, #!2m", 4),
+ "eor", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2AddRRI8, 0xf1100000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "adds", "!0C, !1C, #!2m", 4),
+ "adds", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2AdcRRI8, 0xf1500000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
- "adcs", "!0C, !1C, #!2m", 4),
+ "adcs", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2SubRRI8, 0xf1b00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "subs", "!0C, !1C, #!2m", 4),
+ "subs", "!0C, !1C, #!2m", 4, kFixupNone),
ENCODING_MAP(kThumb2SbcRRI8, 0xf1700000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtModImm, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES | USES_CCODES,
- "sbcs", "!0C, !1C, #!2m", 4),
+ "sbcs", "!0C, !1C, #!2m", 4, kFixupNone),
+ ENCODING_MAP(kThumb2RevRR, 0xfa90f080,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12, // Binary, but rm is stored twice.
+ "rev", "!0C, !1C", 4, kFixupNone),
+ ENCODING_MAP(kThumb2RevshRR, 0xfa90f0b0,
+ kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
+ kFmtUnused, -1, -1,
+ IS_TERTIARY_OP | REG_DEF0_USE12, // Binary, but rm is stored twice.
+ "revsh", "!0C, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2It, 0xbf00,
kFmtBitBlt, 7, 4, kFmtBitBlt, 3, 0, kFmtModImm, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | IS_IT | USES_CCODES,
- "it:!1b", "!0c", 2),
+ "it:!1b", "!0c", 2, kFixupNone),
ENCODING_MAP(kThumb2Fmstat, 0xeef1fa10,
kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND | SETS_CCODES,
- "fmstat", "", 4),
+ "fmstat", "", 4, kFixupNone),
ENCODING_MAP(kThumb2Vcmpd, 0xeeb40b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
- "vcmp.f64", "!0S, !1S", 4),
+ "vcmp.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vcmps, 0xeeb40a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE01,
- "vcmp.f32", "!0s, !1s", 4),
+ "vcmp.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrPcRel12, 0xf8df0000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
- "ldr", "!0C, [r15pc, #!1d]", 4),
+ "ldr", "!0C, [r15pc, #!1d]", 4, kFixupLoad),
ENCODING_MAP(kThumb2BCond, 0xf0008000,
kFmtBrOffset, -1, -1, kFmtBitBlt, 25, 22, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | IS_BRANCH | USES_CCODES | NEEDS_FIXUP,
- "b!1c", "!0t", 4),
+ "b!1c", "!0t", 4, kFixupCondBranch),
ENCODING_MAP(kThumb2Vmovd_RR, 0xeeb00b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f64", "!0S, !1S", 4),
+ "vmov.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovs_RR, 0xeeb00a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vmov.f32", "!0s, !1s", 4),
+ "vmov.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmrs, 0xee100a10,
kFmtBitBlt, 15, 12, kFmtSfp, 7, 16, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "fmrs", "!0C, !1s", 4),
+ "fmrs", "!0C, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmsr, 0xee000a10,
kFmtSfp, 7, 16, kFmtBitBlt, 15, 12, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "fmsr", "!0s, !1C", 4),
+ "fmsr", "!0s, !1C", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmrrd, 0xec500b10,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtDfp, 5, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF01_USE2,
- "fmrrd", "!0C, !1C, !2S", 4),
+ "fmrrd", "!0C, !1C, !2S", 4, kFixupNone),
ENCODING_MAP(kThumb2Fmdrr, 0xec400b10,
kFmtDfp, 5, 0, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12,
- "fmdrr", "!0S, !1C, !2C", 4),
+ "fmdrr", "!0S, !1C, !2C", 4, kFixupNone),
ENCODING_MAP(kThumb2Vabsd, 0xeeb00bc0,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vabs.f64", "!0S, !1S", 4),
+ "vabs.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vabss, 0xeeb00ac0,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vabs.f32", "!0s, !1s", 4),
+ "vabs.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vnegd, 0xeeb10b40,
kFmtDfp, 22, 12, kFmtDfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vneg.f64", "!0S, !1S", 4),
+ "vneg.f64", "!0S, !1S", 4, kFixupNone),
ENCODING_MAP(kThumb2Vnegs, 0xeeb10a40,
kFmtSfp, 22, 12, kFmtSfp, 5, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1,
- "vneg.f32", "!0s, !1s", 4),
+ "vneg.f32", "!0s, !1s", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovs_IMM8, 0xeeb00a00,
kFmtSfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "vmov.f32", "!0s, #0x!1h", 4),
+ "vmov.f32", "!0s, #0x!1h", 4, kFixupNone),
ENCODING_MAP(kThumb2Vmovd_IMM8, 0xeeb00b00,
kFmtDfp, 22, 12, kFmtFPImm, 16, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0,
- "vmov.f64", "!0S, #0x!1h", 4),
+ "vmov.f64", "!0S, #0x!1h", 4, kFixupNone),
ENCODING_MAP(kThumb2Mla, 0xfb000000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtBitBlt, 15, 12,
IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3,
- "mla", "!0C, !1C, !2C, !3C", 4),
+ "mla", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2Umull, 0xfba00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 3, 0,
IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
- "umull", "!0C, !1C, !2C, !3C", 4),
+ "umull", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2Ldrex, 0xe8500f00,
kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1 | IS_LOAD,
- "ldrex", "!0C, [!1C, #!2E]", 4),
+ "ldrex", "!0C, [!1C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Strex, 0xe8400000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 15, 12, kFmtBitBlt, 19, 16,
kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_DEF0_USE12 | IS_STORE,
- "strex", "!0C,!1C, [!2C, #!2E]", 4),
+ "strex", "!0C,!1C, [!2C, #!2E]", 4, kFixupNone),
ENCODING_MAP(kThumb2Clrex, 0xf3bf8f2f,
kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND,
- "clrex", "", 4),
+ "clrex", "", 4, kFixupNone),
ENCODING_MAP(kThumb2Bfi, 0xf3600000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtShift5, -1, -1,
kFmtBitBlt, 4, 0, IS_QUAD_OP | REG_DEF0_USE1,
- "bfi", "!0C,!1C,#!2d,#!3d", 4),
+ "bfi", "!0C,!1C,#!2d,#!3d", 4, kFixupNone),
ENCODING_MAP(kThumb2Bfc, 0xf36f0000,
kFmtBitBlt, 11, 8, kFmtShift5, -1, -1, kFmtBitBlt, 4, 0,
kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0,
- "bfc", "!0C,#!1d,#!2d", 4),
+ "bfc", "!0C,#!1d,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2Dmb, 0xf3bf8f50,
kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP,
- "dmb", "#!0B", 4),
+ "dmb", "#!0B", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrPcReln12, 0xf85f0000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
- "ldr", "!0C, [r15pc, -#!1d]", 4),
+ "ldr", "!0C, [r15pc, -#!1d]", 4, kFixupNone),
ENCODING_MAP(kThumb2Stm, 0xe9000000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 12, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_USE0 | REG_USE_LIST1 | IS_STORE,
- "stm", "!0C, <!1R>", 4),
+ "stm", "!0C, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumbUndefined, 0xde00,
kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND,
- "undefined", "", 2),
+ "undefined", "", 2, kFixupNone),
// NOTE: vpop, vpush hard-encoded for s16+ reg list
ENCODING_MAP(kThumb2VPopCS, 0xecbd8a00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF_FPCS_LIST0
- | IS_LOAD, "vpop", "<!0P>", 4),
+ | IS_LOAD, "vpop", "<!0P>", 4, kFixupNone),
ENCODING_MAP(kThumb2VPushCS, 0xed2d8a00,
kFmtBitBlt, 7, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE_FPCS_LIST0
- | IS_STORE, "vpush", "<!0P>", 4),
+ | IS_STORE, "vpush", "<!0P>", 4, kFixupNone),
ENCODING_MAP(kThumb2Vldms, 0xec900a00,
kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE0 | REG_DEF_FPCS_LIST2
- | IS_LOAD, "vldms", "!0C, <!2Q>", 4),
+ | IS_LOAD, "vldms", "!0C, <!2Q>", 4, kFixupNone),
ENCODING_MAP(kThumb2Vstms, 0xec800a00,
kFmtBitBlt, 19, 16, kFmtSfp, 22, 12, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_USE0 | REG_USE_FPCS_LIST2
- | IS_STORE, "vstms", "!0C, <!2Q>", 4),
+ | IS_STORE, "vstms", "!0C, <!2Q>", 4, kFixupNone),
ENCODING_MAP(kThumb2BUncond, 0xf0009000,
kFmtOff24, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH,
- "b", "!0t", 4),
+ "b", "!0t", 4, kFixupT2Branch),
ENCODING_MAP(kThumb2MovImm16H, 0xf2c00000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0,
- "movt", "!0C, #!1M", 4),
+ "movt", "!0C, #!1M", 4, kFixupNone),
ENCODING_MAP(kThumb2AddPCR, 0x4487,
kFmtBitBlt, 6, 3, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
- IS_UNARY_OP | REG_USE0 | IS_BRANCH,
- "add", "rPC, !0C", 2),
+ IS_UNARY_OP | REG_USE0 | IS_BRANCH | NEEDS_FIXUP,
+ "add", "rPC, !0C", 2, kFixupLabel),
ENCODING_MAP(kThumb2Adr, 0xf20f0000,
kFmtBitBlt, 11, 8, kFmtImm12, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
/* Note: doesn't affect flags */
IS_TERTIARY_OP | REG_DEF0 | NEEDS_FIXUP,
- "adr", "!0C,#!1d", 4),
+ "adr", "!0C,#!1d", 4, kFixupAdr),
ENCODING_MAP(kThumb2MovImm16LST, 0xf2400000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | NEEDS_FIXUP,
- "mov", "!0C, #!1M", 4),
+ "mov", "!0C, #!1M", 4, kFixupMovImmLST),
ENCODING_MAP(kThumb2MovImm16HST, 0xf2c00000,
kFmtBitBlt, 11, 8, kFmtImm16, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0 | REG_USE0 | NEEDS_FIXUP,
- "movt", "!0C, #!1M", 4),
+ "movt", "!0C, #!1M", 4, kFixupMovImmHST),
ENCODING_MAP(kThumb2LdmiaWB, 0xe8b00000,
kFmtBitBlt, 19, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_BINARY_OP | REG_DEF0_USE0 | REG_DEF_LIST1 | IS_LOAD,
- "ldmia", "!0C!!, <!1R>", 4),
+ "ldmia", "!0C!!, <!1R>", 4, kFixupNone),
ENCODING_MAP(kThumb2SubsRRI12, 0xf1b00000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtImm12, -1, -1,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0_USE1 | SETS_CCODES,
- "subs", "!0C,!1C,#!2d", 4),
+ "subs", "!0C,!1C,#!2d", 4, kFixupNone),
ENCODING_MAP(kThumb2OrrRRRs, 0xea500000,
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1, IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "orrs", "!0C, !1C, !2C!3H", 4),
+ "orrs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2Push1, 0xf84d0d04,
kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_USE0
- | IS_STORE, "push1", "!0C", 4),
+ | IS_STORE, "push1", "!0C", 4, kFixupNone),
ENCODING_MAP(kThumb2Pop1, 0xf85d0b04,
kFmtBitBlt, 15, 12, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1,
IS_UNARY_OP | REG_DEF_SP | REG_USE_SP | REG_DEF0
- | IS_LOAD, "pop1", "!0C", 4),
+ | IS_LOAD, "pop1", "!0C", 4, kFixupNone),
ENCODING_MAP(kThumb2RsubRRR, 0xebd00000, /* setflags encoding */
kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0,
kFmtShift, -1, -1,
IS_QUAD_OP | REG_DEF0_USE12 | SETS_CCODES,
- "rsbs", "!0C, !1C, !2C!3H", 4),
+ "rsbs", "!0C, !1C, !2C!3H", 4, kFixupNone),
ENCODING_MAP(kThumb2Smull, 0xfb800000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 3, 0,
IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | REG_USE3,
- "smull", "!0C, !1C, !2C, !3C", 4),
+ "smull", "!0C, !1C, !2C, !3C", 4, kFixupNone),
ENCODING_MAP(kThumb2LdrdPcRel8, 0xe9df0000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 7, 0,
kFmtUnused, -1, -1,
IS_TERTIARY_OP | REG_DEF0 | REG_DEF1 | REG_USE_PC | IS_LOAD | NEEDS_FIXUP,
- "ldrd", "!0C, !1C, [pc, #!2E]", 4),
+ "ldrd", "!0C, !1C, [pc, #!2E]", 4, kFixupLoad),
ENCODING_MAP(kThumb2LdrdI8, 0xe9d00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 7, 0,
IS_QUAD_OP | REG_DEF0 | REG_DEF1 | REG_USE2 | IS_LOAD,
- "ldrd", "!0C, !1C, [!2C, #!3E]", 4),
+ "ldrd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone),
ENCODING_MAP(kThumb2StrdI8, 0xe9c00000,
kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16,
kFmtBitBlt, 7, 0,
IS_QUAD_OP | REG_USE0 | REG_USE1 | REG_USE2 | IS_STORE,
- "strd", "!0C, !1C, [!2C, #!3E]", 4),
+ "strd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone),
};
+// new_lir replaces orig_lir in the pcrel_fixup list.
+void ArmMir2Lir::ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) {
+ new_lir->u.a.pcrel_next = orig_lir->u.a.pcrel_next;
+ if (UNLIKELY(prev_lir == NULL)) {
+ first_fixup_ = new_lir;
+ } else {
+ prev_lir->u.a.pcrel_next = new_lir;
+ }
+ orig_lir->flags.fixup = kFixupNone;
+}
+
+// new_lir is inserted before orig_lir in the pcrel_fixup list.
+void ArmMir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) {
+ new_lir->u.a.pcrel_next = orig_lir;
+ if (UNLIKELY(prev_lir == NULL)) {
+ first_fixup_ = new_lir;
+ } else {
+ DCHECK(prev_lir->u.a.pcrel_next == orig_lir);
+ prev_lir->u.a.pcrel_next = new_lir;
+ }
+}
+
/*
* The fake NOP of moving r0 to r0 actually will incur data stalls if r0 is
* not ready. Since r5FP is not updated often, it is less likely to
@@ -997,398 +1047,641 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = {
*/
#define PADDING_MOV_R5_R5 0x1C2D
-/*
- * Assemble the LIR into binary instruction format. Note that we may
- * discover that pc-relative displacements may not fit the selected
- * instruction.
- */
-AssemblerStatus ArmMir2Lir::AssembleInstructions(uintptr_t start_addr) {
- LIR* lir;
- AssemblerStatus res = kSuccess; // Assume success
-
- for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
- if (lir->opcode < 0) {
- /* 1 means padding is needed */
- if ((lir->opcode == kPseudoPseudoAlign4) && (lir->operands[0] == 1)) {
- code_buffer_.push_back(PADDING_MOV_R5_R5 & 0xFF);
- code_buffer_.push_back((PADDING_MOV_R5_R5 >> 8) & 0xFF);
+void ArmMir2Lir::EncodeLIR(LIR* lir) {
+ int opcode = lir->opcode;
+ if (IsPseudoLirOp(opcode)) {
+ if (UNLIKELY(opcode == kPseudoPseudoAlign4)) {
+ // Note: size for this opcode will be either 0 or 2 depending on final alignment.
+ lir->u.a.bytes[0] = (PADDING_MOV_R5_R5 & 0xff);
+ lir->u.a.bytes[1] = ((PADDING_MOV_R5_R5 >> 8) & 0xff);
+ lir->flags.size = (lir->offset & 0x2);
+ }
+ } else if (LIKELY(!lir->flags.is_nop)) {
+ const ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
+ uint32_t bits = encoder->skeleton;
+ for (int i = 0; i < 4; i++) {
+ uint32_t operand;
+ uint32_t value;
+ operand = lir->operands[i];
+ ArmEncodingKind kind = encoder->field_loc[i].kind;
+ if (LIKELY(kind == kFmtBitBlt)) {
+ value = (operand << encoder->field_loc[i].start) &
+ ((1 << (encoder->field_loc[i].end + 1)) - 1);
+ bits |= value;
+ } else {
+ switch (encoder->field_loc[i].kind) {
+ case kFmtSkip:
+ break; // Nothing to do, but continue to next.
+ case kFmtUnused:
+ i = 4; // Done, break out of the enclosing loop.
+ break;
+ case kFmtFPImm:
+ value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end;
+ value |= (operand & 0x0F) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtBrOffset:
+ value = ((operand & 0x80000) >> 19) << 26;
+ value |= ((operand & 0x40000) >> 18) << 11;
+ value |= ((operand & 0x20000) >> 17) << 13;
+ value |= ((operand & 0x1f800) >> 11) << 16;
+ value |= (operand & 0x007ff);
+ bits |= value;
+ break;
+ case kFmtShift5:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtShift:
+ value = ((operand & 0x70) >> 4) << 12;
+ value |= (operand & 0x0f) << 4;
+ bits |= value;
+ break;
+ case kFmtBWidth:
+ value = operand - 1;
+ bits |= value;
+ break;
+ case kFmtLsb:
+ value = ((operand & 0x1c) >> 2) << 12;
+ value |= (operand & 0x03) << 6;
+ bits |= value;
+ break;
+ case kFmtImm6:
+ value = ((operand & 0x20) >> 5) << 9;
+ value |= (operand & 0x1f) << 3;
+ bits |= value;
+ break;
+ case kFmtDfp: {
+ DCHECK(ARM_DOUBLEREG(operand));
+ DCHECK_EQ((operand & 0x1), 0U);
+ uint32_t reg_name = (operand & ARM_FP_REG_MASK) >> 1;
+ /* Snag the 1-bit slice and position it */
+ value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= (reg_name & 0x0f) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ }
+ case kFmtSfp:
+ DCHECK(ARM_SINGLEREG(operand));
+ /* Snag the 1-bit slice and position it */
+ value = (operand & 0x1) << encoder->field_loc[i].end;
+ /* Extract and position the 4-bit slice */
+ value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start;
+ bits |= value;
+ break;
+ case kFmtImm12:
+ case kFmtModImm:
+ value = ((operand & 0x800) >> 11) << 26;
+ value |= ((operand & 0x700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtImm16:
+ value = ((operand & 0x0800) >> 11) << 26;
+ value |= ((operand & 0xf000) >> 12) << 16;
+ value |= ((operand & 0x0700) >> 8) << 12;
+ value |= operand & 0x0ff;
+ bits |= value;
+ break;
+ case kFmtOff24: {
+ uint32_t signbit = (operand >> 31) & 0x1;
+ uint32_t i1 = (operand >> 22) & 0x1;
+ uint32_t i2 = (operand >> 21) & 0x1;
+ uint32_t imm10 = (operand >> 11) & 0x03ff;
+ uint32_t imm11 = operand & 0x07ff;
+ uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
+ uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
+ value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
+ imm11;
+ bits |= value;
+ }
+ break;
+ default:
+ LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind;
+ }
}
- continue;
}
-
- if (lir->flags.is_nop) {
- continue;
+ if (encoder->size == 4) {
+ lir->u.a.bytes[0] = ((bits >> 16) & 0xff);
+ lir->u.a.bytes[1] = ((bits >> 24) & 0xff);
+ lir->u.a.bytes[2] = (bits & 0xff);
+ lir->u.a.bytes[3] = ((bits >> 8) & 0xff);
+ } else {
+ DCHECK_EQ(encoder->size, 2);
+ lir->u.a.bytes[0] = (bits & 0xff);
+ lir->u.a.bytes[1] = ((bits >> 8) & 0xff);
}
+ lir->flags.size = encoder->size;
+ }
+}
- /*
- * For PC-relative displacements we won't know if the
- * selected instruction will work until late (i.e. - now).
- * If something doesn't fit, we must replace the short-form
- * operation with a longer-form one. Note, though, that this
- * can change code we've already processed, so we'll need to
- * re-calculate offsets and restart. To limit the number of
- * restarts, the entire list will be scanned and patched.
- * Of course, the patching itself may cause new overflows so this
- * is an iterative process.
- */
- if (lir->flags.pcRelFixup) {
- if (lir->opcode == kThumbLdrPcRel ||
- lir->opcode == kThumb2LdrPcRel12 ||
- lir->opcode == kThumbAddPcRel ||
- lir->opcode == kThumb2LdrdPcRel8 ||
- ((lir->opcode == kThumb2Vldrd) && (lir->operands[1] == r15pc)) ||
- ((lir->opcode == kThumb2Vldrs) && (lir->operands[1] == r15pc))) {
- /*
- * PC-relative loads are mostly used to load immediates
- * that are too large to materialize directly in one shot.
- * However, if the load displacement exceeds the limit,
- * we revert to a multiple-instruction materialization sequence.
- */
- LIR *lir_target = lir->target;
- uintptr_t pc = (lir->offset + 4) & ~3;
- uintptr_t target = lir_target->offset;
- int delta = target - pc;
- if (delta & 0x3) {
- LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
- }
- // First, a sanity check for cases we shouldn't see now
- if (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) ||
- ((lir->opcode == kThumbLdrPcRel) && (delta > 1020))) {
- // Shouldn't happen in current codegen.
- LOG(FATAL) << "Unexpected pc-rel offset " << delta;
- }
- // Now, check for the difficult cases
- if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) ||
- ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) ||
- ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) ||
- ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) {
+// Assemble the LIR into binary instruction format.
+void ArmMir2Lir::AssembleLIR() {
+ LIR* lir;
+ LIR* prev_lir;
+ cu_->NewTimingSplit("Assemble");
+ int assembler_retries = 0;
+ CodeOffset starting_offset = EncodeRange(first_lir_insn_, last_lir_insn_, 0);
+ data_offset_ = (starting_offset + 0x3) & ~0x3;
+ int32_t offset_adjustment;
+ AssignDataOffsets();
+
+ /*
+ * Note: generation must be 1 on first pass (to distinguish from initialized state of 0 for non-visited nodes).
+ * Start at zero here, and bit will be flipped to 1 on entry to the loop.
+ */
+ int generation = 0;
+ while (true) {
+ offset_adjustment = 0;
+ AssemblerStatus res = kSuccess; // Assume success
+ generation ^= 1;
+ // Note: nodes requring possible fixup linked in ascending order.
+ lir = first_fixup_;
+ prev_lir = NULL;
+ while (lir != NULL) {
+ /*
+ * NOTE: the lir being considered here will be encoded following the switch (so long as
+ * we're not in a retry situation). However, any new non-pc_rel instructions inserted
+ * due to retry must be explicitly encoded at the time of insertion. Note that
+ * inserted instructions don't need use/def flags, but do need size and pc-rel status
+ * properly updated.
+ */
+ lir->offset += offset_adjustment;
+ // During pass, allows us to tell whether a node has been updated with offset_adjustment yet.
+ lir->flags.generation = generation;
+ switch (static_cast<FixupKind>(lir->flags.fixup)) {
+ case kFixupLabel:
+ case kFixupNone:
+ break;
+ case kFixupVLoad:
+ if (lir->operands[1] != r15pc) {
+ break;
+ }
+ // NOTE: intentional fallthrough.
+ case kFixupLoad: {
/*
- * Note: because rARM_LR may be used to fix up out-of-range
- * vldrs/vldrd we include REG_DEF_LR in the resource
- * masks for these instructions.
+ * PC-relative loads are mostly used to load immediates
+ * that are too large to materialize directly in one shot.
+ * However, if the load displacement exceeds the limit,
+ * we revert to a multiple-instruction materialization sequence.
*/
- int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) || (lir->opcode == kThumb2LdrPcRel12))
- ? lir->operands[0] : rARM_LR;
+ LIR *lir_target = lir->target;
+ CodeOffset pc = (lir->offset + 4) & ~3;
+ CodeOffset target = lir_target->offset +
+ ((lir_target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ if (res != kSuccess) {
+ /*
+ * In this case, we're just estimating and will do it again for real. Ensure offset
+ * is legal.
+ */
+ delta &= ~0x3;
+ }
+ DCHECK_EQ((delta & 0x3), 0);
+ // First, a sanity check for cases we shouldn't see now
+ if (kIsDebugBuild && (((lir->opcode == kThumbAddPcRel) && (delta > 1020)) ||
+ ((lir->opcode == kThumbLdrPcRel) && (delta > 1020)))) {
+ // Shouldn't happen in current codegen.
+ LOG(FATAL) << "Unexpected pc-rel offset " << delta;
+ }
+ // Now, check for the difficult cases
+ if (((lir->opcode == kThumb2LdrPcRel12) && (delta > 4091)) ||
+ ((lir->opcode == kThumb2LdrdPcRel8) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrs) && (delta > 1020)) ||
+ ((lir->opcode == kThumb2Vldrd) && (delta > 1020))) {
+ /*
+ * Note: The reason vldrs/vldrd include rARM_LR in their use/def masks is that we
+ * sometimes have to use it to fix up out-of-range accesses. This is where that
+ * happens.
+ */
+ int base_reg = ((lir->opcode == kThumb2LdrdPcRel8) ||
+ (lir->opcode == kThumb2LdrPcRel12)) ? lir->operands[0] : rARM_LR;
- // Add new Adr to generate the address.
- LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr,
- base_reg, 0, 0, 0, 0, lir->target);
- InsertLIRBefore(lir, new_adr);
+ // Add new Adr to generate the address.
+ LIR* new_adr = RawLIR(lir->dalvik_offset, kThumb2Adr,
+ base_reg, 0, 0, 0, 0, lir->target);
+ new_adr->offset = lir->offset;
+ new_adr->flags.fixup = kFixupAdr;
+ new_adr->flags.size = EncodingMap[kThumb2Adr].size;
+ InsertLIRBefore(lir, new_adr);
+ lir->offset += new_adr->flags.size;
+ offset_adjustment += new_adr->flags.size;
- // Convert to normal load.
- if (lir->opcode == kThumb2LdrPcRel12) {
- lir->opcode = kThumb2LdrRRI12;
- } else if (lir->opcode == kThumb2LdrdPcRel8) {
- lir->opcode = kThumb2LdrdI8;
- }
- // Change the load to be relative to the new Adr base.
- if (lir->opcode == kThumb2LdrdI8) {
- lir->operands[3] = 0;
- lir->operands[2] = base_reg;
+ // lir no longer pcrel, unlink and link in new_adr.
+ ReplaceFixup(prev_lir, lir, new_adr);
+
+ // Convert to normal load.
+ offset_adjustment -= lir->flags.size;
+ if (lir->opcode == kThumb2LdrPcRel12) {
+ lir->opcode = kThumb2LdrRRI12;
+ } else if (lir->opcode == kThumb2LdrdPcRel8) {
+ lir->opcode = kThumb2LdrdI8;
+ }
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ offset_adjustment += lir->flags.size;
+ // Change the load to be relative to the new Adr base.
+ if (lir->opcode == kThumb2LdrdI8) {
+ lir->operands[3] = 0;
+ lir->operands[2] = base_reg;
+ } else {
+ lir->operands[2] = 0;
+ lir->operands[1] = base_reg;
+ }
+ // Must redo encoding here - won't ever revisit this node.
+ EncodeLIR(lir);
+ prev_lir = new_adr; // Continue scan with new_adr;
+ lir = new_adr->u.a.pcrel_next;
+ res = kRetryAll;
+ continue;
} else {
- lir->operands[2] = 0;
- lir->operands[1] = base_reg;
+ if ((lir->opcode == kThumb2Vldrs) ||
+ (lir->opcode == kThumb2Vldrd) ||
+ (lir->opcode == kThumb2LdrdPcRel8)) {
+ lir->operands[2] = delta >> 2;
+ } else {
+ lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta :
+ delta >> 2;
+ }
}
- SetupResourceMasks(lir);
- res = kRetryAll;
- } else {
- if ((lir->opcode == kThumb2Vldrs) ||
- (lir->opcode == kThumb2Vldrd) ||
- (lir->opcode == kThumb2LdrdPcRel8)) {
- lir->operands[2] = delta >> 2;
+ break;
+ }
+ case kFixupCBxZ: {
+ LIR *target_lir = lir->target;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ if (delta > 126 || delta < 0) {
+ /*
+ * Convert to cmp rx,#0 / b[eq/ne] tgt pair
+ * Make new branch instruction and insert after
+ */
+ LIR* new_inst =
+ RawLIR(lir->dalvik_offset, kThumbBCond, 0,
+ (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe,
+ 0, 0, 0, lir->target);
+ InsertLIRAfter(lir, new_inst);
+
+ /* Convert the cb[n]z to a cmp rx, #0 ] */
+ // Subtract the old size.
+ offset_adjustment -= lir->flags.size;
+ lir->opcode = kThumbCmpRI8;
+ /* operand[0] is src1 in both cb[n]z & CmpRI8 */
+ lir->operands[1] = 0;
+ lir->target = 0;
+ EncodeLIR(lir); // NOTE: sets flags.size.
+ // Add back the new size.
+ DCHECK_EQ(lir->flags.size, static_cast<uint32_t>(EncodingMap[lir->opcode].size));
+ offset_adjustment += lir->flags.size;
+ // Set up the new following inst.
+ new_inst->offset = lir->offset + lir->flags.size;
+ new_inst->flags.fixup = kFixupCondBranch;
+ new_inst->flags.size = EncodingMap[new_inst->opcode].size;
+ offset_adjustment += new_inst->flags.size;
+
+ // lir no longer pcrel, unlink and link in new_inst.
+ ReplaceFixup(prev_lir, lir, new_inst);
+ prev_lir = new_inst; // Continue with the new instruction.
+ lir = new_inst->u.a.pcrel_next;
+ res = kRetryAll;
+ continue;
} else {
- lir->operands[1] = (lir->opcode == kThumb2LdrPcRel12) ? delta :
- delta >> 2;
+ lir->operands[1] = delta >> 1;
}
+ break;
}
- } else if (lir->opcode == kThumb2Cbnz || lir->opcode == kThumb2Cbz) {
- LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- int delta = target - pc;
- if (delta > 126 || delta < 0) {
- /*
- * Convert to cmp rx,#0 / b[eq/ne] tgt pair
- * Make new branch instruction and insert after
- */
- LIR* new_inst =
- RawLIR(lir->dalvik_offset, kThumbBCond, 0,
- (lir->opcode == kThumb2Cbz) ? kArmCondEq : kArmCondNe,
- 0, 0, 0, lir->target);
- InsertLIRAfter(lir, new_inst);
- /* Convert the cb[n]z to a cmp rx, #0 ] */
- lir->opcode = kThumbCmpRI8;
- /* operand[0] is src1 in both cb[n]z & CmpRI8 */
- lir->operands[1] = 0;
- lir->target = 0;
- SetupResourceMasks(lir);
- res = kRetryAll;
- } else {
- lir->operands[1] = delta >> 1;
- }
- } else if (lir->opcode == kThumb2Push || lir->opcode == kThumb2Pop) {
- if (__builtin_popcount(lir->operands[0]) == 1) {
- /*
- * The standard push/pop multiple instruction
- * requires at least two registers in the list.
- * If we've got just one, switch to the single-reg
- * encoding.
- */
- lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 :
- kThumb2Pop1;
- int reg = 0;
- while (lir->operands[0]) {
- if (lir->operands[0] & 0x1) {
- break;
- } else {
- reg++;
- lir->operands[0] >>= 1;
+ case kFixupPushPop: {
+ if (__builtin_popcount(lir->operands[0]) == 1) {
+ /*
+ * The standard push/pop multiple instruction
+ * requires at least two registers in the list.
+ * If we've got just one, switch to the single-reg
+ * encoding.
+ */
+ lir->opcode = (lir->opcode == kThumb2Push) ? kThumb2Push1 :
+ kThumb2Pop1;
+ int reg = 0;
+ while (lir->operands[0]) {
+ if (lir->operands[0] & 0x1) {
+ break;
+ } else {
+ reg++;
+ lir->operands[0] >>= 1;
+ }
}
+ lir->operands[0] = reg;
+ // This won't change again, don't bother unlinking, just reset fixup kind
+ lir->flags.fixup = kFixupNone;
}
- lir->operands[0] = reg;
- SetupResourceMasks(lir);
- res = kRetryAll;
- }
- } else if (lir->opcode == kThumbBCond || lir->opcode == kThumb2BCond) {
- LIR *target_lir = lir->target;
- int delta = 0;
- DCHECK(target_lir);
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- delta = target - pc;
- if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
- lir->opcode = kThumb2BCond;
- SetupResourceMasks(lir);
- res = kRetryAll;
+ break;
}
- lir->operands[0] = delta >> 1;
- } else if (lir->opcode == kThumb2BUncond) {
- LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- int delta = target - pc;
- lir->operands[0] = delta >> 1;
- if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
- lir->operands[0] == 0) { // Useless branch
- lir->flags.is_nop = true;
- res = kRetryAll;
+ case kFixupCondBranch: {
+ LIR *target_lir = lir->target;
+ int32_t delta = 0;
+ DCHECK(target_lir);
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ delta = target - pc;
+ if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+ offset_adjustment -= lir->flags.size;
+ lir->opcode = kThumb2BCond;
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ // Fixup kind remains the same.
+ offset_adjustment += lir->flags.size;
+ res = kRetryAll;
+ }
+ lir->operands[0] = delta >> 1;
+ break;
}
- } else if (lir->opcode == kThumbBUncond) {
- LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
- int delta = target - pc;
- if (delta > 2046 || delta < -2048) {
- // Convert to Thumb2BCond w/ kArmCondAl
- lir->opcode = kThumb2BUncond;
- lir->operands[0] = 0;
- SetupResourceMasks(lir);
- res = kRetryAll;
- } else {
+ case kFixupT2Branch: {
+ LIR *target_lir = lir->target;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
lir->operands[0] = delta >> 1;
- if (!(cu_->disable_opt & (1 << kSafeOptimizations)) &&
- lir->operands[0] == -1) { // Useless branch
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == 0) {
+ // Useless branch
+ offset_adjustment -= lir->flags.size;
lir->flags.is_nop = true;
+ // Don't unlink - just set to do-nothing.
+ lir->flags.fixup = kFixupNone;
+ res = kRetryAll;
+ }
+ break;
+ }
+ case kFixupT1Branch: {
+ LIR *target_lir = lir->target;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset +
+ ((target_lir->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t delta = target - pc;
+ if (delta > 2046 || delta < -2048) {
+ // Convert to Thumb2BCond w/ kArmCondAl
+ offset_adjustment -= lir->flags.size;
+ lir->opcode = kThumb2BUncond;
+ lir->operands[0] = 0;
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ lir->flags.fixup = kFixupT2Branch;
+ offset_adjustment += lir->flags.size;
res = kRetryAll;
+ } else {
+ lir->operands[0] = delta >> 1;
+ if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && lir->operands[0] == -1) {
+ // Useless branch
+ offset_adjustment -= lir->flags.size;
+ lir->flags.is_nop = true;
+ // Don't unlink - just set to do-nothing.
+ lir->flags.fixup = kFixupNone;
+ res = kRetryAll;
+ }
}
+ break;
}
- } else if (lir->opcode == kThumbBlx1) {
- DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2);
- /* cur_pc is Thumb */
- uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
- uintptr_t target = lir->operands[1];
+ case kFixupBlx1: {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBlx2);
+ /* cur_pc is Thumb */
+ CodeOffset cur_pc = (lir->offset + 4) & ~3;
+ CodeOffset target = lir->operands[1];
- /* Match bit[1] in target with base */
- if (cur_pc & 0x2) {
- target |= 0x2;
- }
- int delta = target - cur_pc;
- DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+ /* Match bit[1] in target with base */
+ if (cur_pc & 0x2) {
+ target |= 0x2;
+ }
+ int32_t delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
- lir->operands[0] = (delta >> 12) & 0x7ff;
- NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
- } else if (lir->opcode == kThumbBl1) {
- DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2);
- /* Both cur_pc and target are Thumb */
- uintptr_t cur_pc = start_addr + lir->offset + 4;
- uintptr_t target = lir->operands[1];
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ break;
+ }
+ case kFixupBl1: {
+ DCHECK(NEXT_LIR(lir)->opcode == kThumbBl2);
+ /* Both cur_pc and target are Thumb */
+ CodeOffset cur_pc = lir->offset + 4;
+ CodeOffset target = lir->operands[1];
- int delta = target - cur_pc;
- DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
+ int32_t delta = target - cur_pc;
+ DCHECK((delta >= -(1<<22)) && (delta <= ((1<<22)-2)));
- lir->operands[0] = (delta >> 12) & 0x7ff;
- NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
- } else if (lir->opcode == kThumb2Adr) {
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[2]);
- LIR* target = lir->target;
- int target_disp = tab_rec ? tab_rec->offset
- : target->offset;
- int disp = target_disp - ((lir->offset + 4) & ~3);
- if (disp < 4096) {
- lir->operands[1] = disp;
- } else {
- // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0]
- // TUNING: if this case fires often, it can be improved. Not expected to be common.
- LIR *new_mov16L =
- RawLIR(lir->dalvik_offset, kThumb2MovImm16LST,
- lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
- reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
- InsertLIRBefore(lir, new_mov16L);
- LIR *new_mov16H =
- RawLIR(lir->dalvik_offset, kThumb2MovImm16HST,
- lir->operands[0], 0, reinterpret_cast<uintptr_t>(lir),
- reinterpret_cast<uintptr_t>(tab_rec), 0, lir->target);
- InsertLIRBefore(lir, new_mov16H);
- if (ARM_LOWREG(lir->operands[0])) {
- lir->opcode = kThumbAddRRLH;
+ lir->operands[0] = (delta >> 12) & 0x7ff;
+ NEXT_LIR(lir)->operands[0] = (delta>> 1) & 0x7ff;
+ break;
+ }
+ case kFixupAdr: {
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[2]));
+ LIR* target = lir->target;
+ int32_t target_disp = (tab_rec != NULL) ? tab_rec->offset + offset_adjustment
+ : target->offset + ((target->flags.generation == lir->flags.generation) ? 0 : offset_adjustment);
+ int32_t disp = target_disp - ((lir->offset + 4) & ~3);
+ if (disp < 4096) {
+ lir->operands[1] = disp;
} else {
- lir->opcode = kThumbAddRRHH;
+ // convert to ldimm16l, ldimm16h, add tgt, pc, operands[0]
+ // TUNING: if this case fires often, it can be improved. Not expected to be common.
+ LIR *new_mov16L =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, lir->operands[0], 0,
+ WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target);
+ new_mov16L->flags.size = EncodingMap[new_mov16L->opcode].size;
+ new_mov16L->flags.fixup = kFixupMovImmLST;
+ new_mov16L->offset = lir->offset;
+ // Link the new instruction, retaining lir.
+ InsertLIRBefore(lir, new_mov16L);
+ lir->offset += new_mov16L->flags.size;
+ offset_adjustment += new_mov16L->flags.size;
+ InsertFixupBefore(prev_lir, lir, new_mov16L);
+ prev_lir = new_mov16L; // Now we've got a new prev.
+ LIR *new_mov16H =
+ RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, lir->operands[0], 0,
+ WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target);
+ new_mov16H->flags.size = EncodingMap[new_mov16H->opcode].size;
+ new_mov16H->flags.fixup = kFixupMovImmHST;
+ new_mov16H->offset = lir->offset;
+ // Link the new instruction, retaining lir.
+ InsertLIRBefore(lir, new_mov16H);
+ lir->offset += new_mov16H->flags.size;
+ offset_adjustment += new_mov16H->flags.size;
+ InsertFixupBefore(prev_lir, lir, new_mov16H);
+ prev_lir = new_mov16H; // Now we've got a new prev.
+
+ offset_adjustment -= lir->flags.size;
+ if (ARM_LOWREG(lir->operands[0])) {
+ lir->opcode = kThumbAddRRLH;
+ } else {
+ lir->opcode = kThumbAddRRHH;
+ }
+ lir->operands[1] = rARM_PC;
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ offset_adjustment += lir->flags.size;
+ // Must stay in fixup list and have offset updated; will be used by LST/HSP pair.
+ lir->flags.fixup = kFixupNone;
+ res = kRetryAll;
}
- lir->operands[1] = rARM_PC;
- SetupResourceMasks(lir);
- res = kRetryAll;
- }
- } else if (lir->opcode == kThumb2MovImm16LST) {
- // operands[1] should hold disp, [2] has add, [3] has tab_rec
- LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
- // If tab_rec is null, this is a literal load. Use target
- LIR* target = lir->target;
- int target_disp = tab_rec ? tab_rec->offset : target->offset;
- lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff;
- } else if (lir->opcode == kThumb2MovImm16HST) {
- // operands[1] should hold disp, [2] has add, [3] has tab_rec
- LIR *addPCInst = reinterpret_cast<LIR*>(lir->operands[2]);
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
- // If tab_rec is null, this is a literal load. Use target
- LIR* target = lir->target;
- int target_disp = tab_rec ? tab_rec->offset : target->offset;
- lir->operands[1] =
- ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff;
- }
- }
- /*
- * If one of the pc-relative instructions expanded we'll have
- * to make another pass. Don't bother to fully assemble the
- * instruction.
- */
- if (res != kSuccess) {
- continue;
- }
- const ArmEncodingMap *encoder = &EncodingMap[lir->opcode];
- uint32_t bits = encoder->skeleton;
- int i;
- for (i = 0; i < 4; i++) {
- uint32_t operand;
- uint32_t value;
- operand = lir->operands[i];
- switch (encoder->field_loc[i].kind) {
- case kFmtUnused:
- break;
- case kFmtFPImm:
- value = ((operand & 0xF0) >> 4) << encoder->field_loc[i].end;
- value |= (operand & 0x0F) << encoder->field_loc[i].start;
- bits |= value;
- break;
- case kFmtBrOffset:
- value = ((operand & 0x80000) >> 19) << 26;
- value |= ((operand & 0x40000) >> 18) << 11;
- value |= ((operand & 0x20000) >> 17) << 13;
- value |= ((operand & 0x1f800) >> 11) << 16;
- value |= (operand & 0x007ff);
- bits |= value;
- break;
- case kFmtShift5:
- value = ((operand & 0x1c) >> 2) << 12;
- value |= (operand & 0x03) << 6;
- bits |= value;
- break;
- case kFmtShift:
- value = ((operand & 0x70) >> 4) << 12;
- value |= (operand & 0x0f) << 4;
- bits |= value;
- break;
- case kFmtBWidth:
- value = operand - 1;
- bits |= value;
- break;
- case kFmtLsb:
- value = ((operand & 0x1c) >> 2) << 12;
- value |= (operand & 0x03) << 6;
- bits |= value;
- break;
- case kFmtImm6:
- value = ((operand & 0x20) >> 5) << 9;
- value |= (operand & 0x1f) << 3;
- bits |= value;
- break;
- case kFmtBitBlt:
- value = (operand << encoder->field_loc[i].start) &
- ((1 << (encoder->field_loc[i].end + 1)) - 1);
- bits |= value;
- break;
- case kFmtDfp: {
- DCHECK(ARM_DOUBLEREG(operand));
- DCHECK_EQ((operand & 0x1), 0U);
- int reg_name = (operand & ARM_FP_REG_MASK) >> 1;
- /* Snag the 1-bit slice and position it */
- value = ((reg_name & 0x10) >> 4) << encoder->field_loc[i].end;
- /* Extract and position the 4-bit slice */
- value |= (reg_name & 0x0f) << encoder->field_loc[i].start;
- bits |= value;
break;
}
- case kFmtSfp:
- DCHECK(ARM_SINGLEREG(operand));
- /* Snag the 1-bit slice and position it */
- value = (operand & 0x1) << encoder->field_loc[i].end;
- /* Extract and position the 4-bit slice */
- value |= ((operand & 0x1e) >> 1) << encoder->field_loc[i].start;
- bits |= value;
+ case kFixupMovImmLST: {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2]));
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int32_t target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] = (target_disp - (addPCInst->offset + 4)) & 0xffff;
break;
- case kFmtImm12:
- case kFmtModImm:
- value = ((operand & 0x800) >> 11) << 26;
- value |= ((operand & 0x700) >> 8) << 12;
- value |= operand & 0x0ff;
- bits |= value;
- break;
- case kFmtImm16:
- value = ((operand & 0x0800) >> 11) << 26;
- value |= ((operand & 0xf000) >> 12) << 16;
- value |= ((operand & 0x0700) >> 8) << 12;
- value |= operand & 0x0ff;
- bits |= value;
+ }
+ case kFixupMovImmHST: {
+ // operands[1] should hold disp, [2] has add, [3] has tab_rec
+ LIR *addPCInst = reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2]));
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
+ // If tab_rec is null, this is a literal load. Use target
+ LIR* target = lir->target;
+ int32_t target_disp = tab_rec ? tab_rec->offset : target->offset;
+ lir->operands[1] =
+ ((target_disp - (addPCInst->offset + 4)) >> 16) & 0xffff;
break;
- case kFmtOff24: {
- uint32_t signbit = (operand >> 31) & 0x1;
- uint32_t i1 = (operand >> 22) & 0x1;
- uint32_t i2 = (operand >> 21) & 0x1;
- uint32_t imm10 = (operand >> 11) & 0x03ff;
- uint32_t imm11 = operand & 0x07ff;
- uint32_t j1 = (i1 ^ signbit) ? 0 : 1;
- uint32_t j2 = (i2 ^ signbit) ? 0 : 1;
- value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) |
- imm11;
- bits |= value;
+ }
+ case kFixupAlign4: {
+ int32_t required_size = lir->offset & 0x2;
+ if (lir->flags.size != required_size) {
+ offset_adjustment += required_size - lir->flags.size;
+ lir->flags.size = required_size;
+ res = kRetryAll;
}
break;
+ }
default:
- LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind;
+ LOG(FATAL) << "Unexpected case " << lir->flags.fixup;
}
+ /*
+ * If one of the pc-relative instructions expanded we'll have
+ * to make another pass. Don't bother to fully assemble the
+ * instruction.
+ */
+ if (res == kSuccess) {
+ EncodeLIR(lir);
+ if (assembler_retries == 0) {
+ // Go ahead and fix up the code buffer image.
+ for (int i = 0; i < lir->flags.size; i++) {
+ code_buffer_[lir->offset + i] = lir->u.a.bytes[i];
+ }
+ }
+ }
+ prev_lir = lir;
+ lir = lir->u.a.pcrel_next;
}
- if (encoder->size == 4) {
- code_buffer_.push_back((bits >> 16) & 0xff);
- code_buffer_.push_back((bits >> 24) & 0xff);
+
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ starting_offset += offset_adjustment;
+ data_offset_ = (starting_offset + 0x3) & ~0x3;
+ AssignDataOffsets();
}
- code_buffer_.push_back(bits & 0xff);
- code_buffer_.push_back((bits >> 8) & 0xff);
}
- return res;
+
+ // Rebuild the CodeBuffer if we had to retry; otherwise it should be good as-is.
+ if (assembler_retries != 0) {
+ code_buffer_.clear();
+ for (LIR* lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ if (lir->flags.is_nop) {
+ continue;
+ } else {
+ for (int i = 0; i < lir->flags.size; i++) {
+ code_buffer_.push_back(lir->u.a.bytes[i]);
+ }
+ }
+ }
+ }
+
+ data_offset_ = (code_buffer_.size() + 0x3) & ~0x3;
+
+ cu_->NewTimingSplit("LiteralData");
+ // Install literals
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ cu_->NewTimingSplit("PcMappingTable");
+ CreateMappingTables();
+
+ cu_->NewTimingSplit("GcMap");
+ CreateNativeGcMap();
}
int ArmMir2Lir::GetInsnSize(LIR* lir) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
return EncodingMap[lir->opcode].size;
}
+// Encode instruction bit pattern and assign offsets.
+uint32_t ArmMir2Lir::EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t offset) {
+ LIR* end_lir = tail_lir->next;
+
+ /*
+ * A significant percentage of methods can be assembled in a single pass. We'll
+ * go ahead and build the code image here, leaving holes for pc-relative fixup
+ * codes. If the code size changes during that pass, we'll have to throw away
+ * this work - but if not, we're ready to go.
+ */
+ code_buffer_.reserve(estimated_native_code_size_ + 256); // Add a little slop.
+ LIR* last_fixup = NULL;
+ for (LIR* lir = head_lir; lir != end_lir; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (!lir->flags.is_nop) {
+ if (lir->flags.fixup != kFixupNone) {
+ if (!IsPseudoLirOp(lir->opcode)) {
+ lir->flags.size = EncodingMap[lir->opcode].size;
+ lir->flags.fixup = EncodingMap[lir->opcode].fixup;
+ } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) {
+ lir->flags.size = (offset & 0x2);
+ lir->flags.fixup = kFixupAlign4;
+ } else {
+ lir->flags.size = 0;
+ lir->flags.fixup = kFixupLabel;
+ }
+ // Link into the fixup chain.
+ lir->flags.use_def_invalid = true;
+ lir->u.a.pcrel_next = NULL;
+ if (first_fixup_ == NULL) {
+ first_fixup_ = lir;
+ } else {
+ last_fixup->u.a.pcrel_next = lir;
+ }
+ last_fixup = lir;
+ } else {
+ EncodeLIR(lir);
+ }
+ for (int i = 0; i < lir->flags.size; i++) {
+ code_buffer_.push_back(lir->u.a.bytes[i]);
+ }
+ offset += lir->flags.size;
+ }
+ }
+ return offset;
+}
+
+void ArmMir2Lir::AssignDataOffsets() {
+ /* Set up offsets for literals */
+ CodeOffset offset = data_offset_;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ total_size_ = AssignFillArrayDataOffset(offset);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 2dbe5f5c36..51aca8540c 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -92,7 +92,7 @@ void ArmMir2Lir::LockLiveArgs(MIR* mir) {
}
/* Find the next MIR, which may be in a following basic block */
-// TODO: should this be a utility in mir_graph?
+// TODO: make this a utility in mir_graph.
MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) {
BasicBlock* bb = *p_bb;
MIR* orig_mir = mir;
@@ -103,7 +103,7 @@ MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) {
if (mir != NULL) {
return mir;
} else {
- bb = bb->fall_through;
+ bb = mir_graph_->GetBasicBlock(bb->fall_through);
*p_bb = bb;
if (bb) {
mir = bb->first_mir_insn;
@@ -120,17 +120,18 @@ MIR* ArmMir2Lir::GetNextMir(BasicBlock** p_bb, MIR* mir) {
// TODO: move to common code
void ArmMir2Lir::GenPrintLabel(MIR* mir) {
/* Mark the beginning of a Dalvik instruction for line tracking */
- char* inst_str = cu_->verbose ?
- mir_graph_->GetDalvikDisassembly(mir) : NULL;
- MarkBoundary(mir->offset, inst_str);
+ if (cu_->verbose) {
+ char* inst_str = mir_graph_->GetDalvikDisassembly(mir);
+ MarkBoundary(mir->offset, inst_str);
+ }
}
MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir,
OpSize size, bool long_or_double, bool is_object) {
- int field_offset;
+ int32_t field_offset;
bool is_volatile;
uint32_t field_idx = mir->dalvikInsn.vC;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile);
if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
return NULL;
}
@@ -152,10 +153,10 @@ MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir,
MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir,
OpSize size, bool long_or_double, bool is_object) {
- int field_offset;
+ int32_t field_offset;
bool is_volatile;
uint32_t field_idx = mir->dalvikInsn.vC;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile);
if (!fast_path || !(mir->optimization_flags & MIR_IGNORE_NULL_CHECK)) {
return NULL;
}
@@ -319,9 +320,9 @@ void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
- int size = table[1];
+ uint32_t size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
- ArenaAllocator::kAllocLIR));
+ ArenaAllocator::kAllocLIR));
switch_tables_.Insert(tab_rec);
// Get the switch value
@@ -337,7 +338,7 @@ void ArmMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
r_key = tmp;
}
// Materialize a pointer to the switch table
- NewLIR3(kThumb2Adr, rBase, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR3(kThumb2Adr, rBase, 0, WrapPointer(tab_rec));
// Set up r_idx
int r_idx = AllocTemp();
LoadConstant(r_idx, size);
@@ -367,7 +368,7 @@ void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
- int size = table[1];
+ uint32_t size = table[1];
tab_rec->targets =
static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*), ArenaAllocator::kAllocLIR));
switch_tables_.Insert(tab_rec);
@@ -376,7 +377,7 @@ void ArmMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
rl_src = LoadValue(rl_src, kCoreReg);
int table_base = AllocTemp();
// Materialize a pointer to the switch table
- NewLIR3(kThumb2Adr, table_base, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR3(kThumb2Adr, table_base, 0, WrapPointer(tab_rec));
int low_key = s4FromSwitchData(&table[2]);
int keyReg;
// Remove the bias, if necessary
@@ -432,95 +433,127 @@ void ArmMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData).Int32Value(),
rARM_LR);
// Materialize a pointer to the fill data image
- NewLIR3(kThumb2Adr, r1, 0, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR3(kThumb2Adr, r1, 0, WrapPointer(tab_rec));
ClobberCalleeSave();
LIR* call_inst = OpReg(kOpBlx, rARM_LR);
MarkSafepointPC(call_inst);
}
/*
- * Handle simple case (thin lock) inline. If it's complicated, bail
- * out to the heavyweight lock/unlock routines. We'll use dedicated
- * registers here in order to be in the right position in case we
- * to bail to oat[Lock/Unlock]Object(self, object)
- *
- * r0 -> self pointer [arg0 for oat[Lock/Unlock]Object
- * r1 -> object [arg1 for oat[Lock/Unlock]Object
- * r2 -> intial contents of object->lock, later result of strex
- * r3 -> self->thread_id
- * r12 -> allow to be used by utilities as general temp
- *
- * The result of the strex is 0 if we acquire the lock.
- *
- * See comments in monitor.cc for the layout of the lock word.
- * Of particular interest to this code is the test for the
- * simple case - which we handle inline. For monitor enter, the
- * simple case is thin lock, held by no-one. For monitor exit,
- * the simple case is thin lock, held by the unlocking thread with
- * a recurse count of 0.
- *
- * A minor complication is that there is a field in the lock word
- * unrelated to locking: the hash state. This field must be ignored, but
- * preserved.
- *
+ * Handle unlocked -> thin locked transition inline or else call out to quick entrypoint. For more
+ * details see monitor.cc.
*/
void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
FlushAllRegs();
- DCHECK_EQ(LW_SHAPE_THIN, 0);
LoadValueDirectFixed(rl_src, r0); // Get obj
LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
- LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
- NewLIR3(kThumb2Ldrex, r1, r0,
- mirror::Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
- // Align owner
- OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
- // Is lock unheld on lock or held by us (==thread_id) on unlock?
- NewLIR4(kThumb2Bfi, r2, r1, 0, LW_LOCK_OWNER_SHIFT - 1);
- NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
- OpRegImm(kOpCmp, r1, 0);
- OpIT(kCondEq, "");
- NewLIR4(kThumb2Strex, r1, r2, r0,
- mirror::Object::MonitorOffset().Int32Value() >> 2);
- OpRegImm(kOpCmp, r1, 0);
- OpIT(kCondNe, "T");
- // Go expensive route - artLockObjectFromCode(self, obj);
- LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR);
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, rARM_LR);
- MarkSafepointPC(call_inst);
- GenMemBarrier(kLoadLoad);
+ constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15.
+ if (kArchVariantHasGoodBranchPredictor) {
+ LIR* null_check_branch;
+ if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
+ null_check_branch = nullptr; // No null check.
+ } else {
+ // If the null-check fails its handled by the slow-path to reduce exception related meta-data.
+ null_check_branch = OpCmpImmBranch(kCondEq, r0, 0, NULL);
+ }
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, r1, 0, NULL);
+ NewLIR4(kThumb2Strex, r1, r2, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ LIR* lock_success_branch = OpCmpImmBranch(kCondEq, r1, 0, NULL);
+
+
+ LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+ not_unlocked_branch->target = slow_path_target;
+ if (null_check_branch != nullptr) {
+ null_check_branch->target = slow_path_target;
+ }
+ // TODO: move to a slow path.
+ // Go expensive route - artLockObjectFromCode(obj);
+ LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+
+ LIR* success_target = NewLIR0(kPseudoTargetLabel);
+ lock_success_branch->target = success_target;
+ GenMemBarrier(kLoadLoad);
+ } else {
+ // Explicit null-check as slow-path is entered using an IT.
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ NewLIR3(kThumb2Ldrex, r1, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondEq, "");
+ NewLIR4(kThumb2Strex/*eq*/, r1, r2, r0, mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpRegImm(kOpCmp, r1, 0);
+ OpIT(kCondNe, "T");
+ // Go expensive route - artLockObjectFromCode(self, obj);
+ LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pLockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kLoadLoad);
+ }
}
/*
- * For monitor unlock, we don't have to use ldrex/strex. Once
- * we've determined that the lock is thin and that we own it with
- * a zero recursion count, it's safe to punch it back to the
- * initial, unlock thin state with a store word.
+ * Handle thin locked -> unlocked transition inline or else call out to quick entrypoint. For more
+ * details see monitor.cc. Note the code below doesn't use ldrex/strex as the code holds the lock
+ * and can only give away ownership if its suspended.
*/
void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
- DCHECK_EQ(LW_SHAPE_THIN, 0);
FlushAllRegs();
LoadValueDirectFixed(rl_src, r0); // Get obj
LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
- LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock
+ LIR* null_check_branch;
LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
- // Is lock unheld on lock or held by us (==thread_id) on unlock?
- OpRegRegImm(kOpAnd, r3, r1,
- (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
- // Align owner
- OpRegImm(kOpLsl, r2, LW_LOCK_OWNER_SHIFT);
- NewLIR3(kThumb2Bfc, r1, LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
- OpRegReg(kOpSub, r1, r2);
- OpIT(kCondEq, "EE");
- StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
- // Go expensive route - UnlockObjectFromCode(obj);
- LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR);
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, rARM_LR);
- MarkSafepointPC(call_inst);
- GenMemBarrier(kStoreLoad);
+ constexpr bool kArchVariantHasGoodBranchPredictor = false; // TODO: true if cortex-A15.
+ if (kArchVariantHasGoodBranchPredictor) {
+ if ((opt_flags & MIR_IGNORE_NULL_CHECK) && !(cu_->disable_opt & (1 << kNullCheckElimination))) {
+ null_check_branch = nullptr; // No null check.
+ } else {
+ // If the null-check fails its handled by the slow-path to reduce exception related meta-data.
+ null_check_branch = OpCmpImmBranch(kCondEq, r0, 0, NULL);
+ }
+ LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1);
+ LoadConstantNoClobber(r3, 0);
+ LIR* slow_unlock_branch = OpCmpBranch(kCondNe, r1, r2, NULL);
+ StoreWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
+ LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
+
+ LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+ slow_unlock_branch->target = slow_path_target;
+ if (null_check_branch != nullptr) {
+ null_check_branch->target = slow_path_target;
+ }
+ // TODO: move to a slow path.
+ // Go expensive route - artUnlockObjectFromCode(obj);
+ LoadWordDisp(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx, rARM_LR);
+ MarkSafepointPC(call_inst);
+
+ LIR* success_target = NewLIR0(kPseudoTargetLabel);
+ unlock_success_branch->target = success_target;
+ GenMemBarrier(kStoreLoad);
+ } else {
+ // Explicit null-check as slow-path is entered using an IT.
+ GenNullCheck(rl_src.s_reg_low, r0, opt_flags);
+ LoadWordDisp(r0, mirror::Object::MonitorOffset().Int32Value(), r1); // Get lock
+ LoadWordDisp(rARM_SELF, Thread::ThinLockIdOffset().Int32Value(), r2);
+ LoadConstantNoClobber(r3, 0);
+ // Is lock unheld on lock or held by us (==thread_id) on unlock?
+ OpRegReg(kOpCmp, r1, r2);
+ OpIT(kCondEq, "EE");
+ StoreWordDisp/*eq*/(r0, mirror::Object::MonitorOffset().Int32Value(), r3);
+ // Go expensive route - UnlockObjectFromCode(obj);
+ LoadWordDisp/*ne*/(rARM_SELF, QUICK_ENTRYPOINT_OFFSET(pUnlockObject).Int32Value(), rARM_LR);
+ ClobberCalleeSave();
+ LIR* call_inst = OpReg(kOpBlx/*ne*/, rARM_LR);
+ MarkSafepointPC(call_inst);
+ GenMemBarrier(kStoreLoad);
+ }
}
void ArmMir2Lir::GenMoveException(RegLocation rl_dest) {
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 291319f258..15355be9d7 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -51,7 +51,6 @@ class ArmMir2Lir : public Mir2Lir {
int AllocTypedTempPair(bool fp_hint, int reg_class);
int S2d(int low_reg, int high_reg);
int TargetReg(SpecialTargetRegister reg);
- RegisterInfo* GetRegInfo(int reg);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -71,9 +70,13 @@ class ArmMir2Lir : public Mir2Lir {
void CompilerInitializeRegAlloc();
// Required for target - miscellaneous.
- AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void AssembleLIR();
+ uint32_t EncodeRange(LIR* head_lir, LIR* tail_lir, uint32_t starting_offset);
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ void EncodeLIR(LIR* lir);
void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
- void SetupTargetResourceMasks(LIR* lir);
+ void SetupTargetResourceMasks(LIR* lir, uint64_t flags);
const char* GetTargetInstFmt(int opcode);
const char* GetTargetInstName(int opcode);
std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
@@ -85,12 +88,10 @@ class ArmMir2Lir : public Mir2Lir {
// Required for target - Dalvik-level generators.
void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2);
- void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
- RegLocation rl_src, int scale);
void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale);
- void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
+ void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src, int scale, bool card_mark);
void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_shift);
void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -106,6 +107,8 @@ class ArmMir2Lir : public Mir2Lir {
bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
bool GenInlinedSqrt(CallInfo* info);
+ bool GenInlinedPeek(CallInfo* info, OpSize size);
+ bool GenInlinedPoke(CallInfo* info, OpSize size);
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -118,7 +121,7 @@ class ArmMir2Lir : public Mir2Lir {
void GenDivZeroCheck(int reg_lo, int reg_hi);
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
- void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
@@ -130,8 +133,8 @@ class ArmMir2Lir : public Mir2Lir {
int first_bit, int second_bit);
void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
- void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
- void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
// Required for target - single operation generators.
@@ -188,6 +191,9 @@ class ArmMir2Lir : public Mir2Lir {
MIR* SpecialIdentity(MIR* mir);
LIR* LoadFPConstantValue(int r_dest, int value);
bool BadOverlap(RegLocation rl_src, RegLocation rl_dest);
+ void ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir);
+ void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir);
+ void AssignDataOffsets();
};
} // namespace art
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index 08d6778129..480e0218d5 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -176,7 +176,7 @@ void ArmMir2Lir::GenConversion(Instruction::Code opcode,
void ArmMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
bool is_double) {
- LIR* target = &block_label_list_[bb->taken->id];
+ LIR* target = &block_label_list_[bb->taken];
RegLocation rl_src1;
RegLocation rl_src2;
if (is_double) {
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 6fbdd2fd49..42bf3d4d00 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -24,8 +24,7 @@
namespace art {
-LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1,
- int src2, LIR* target) {
+LIR* ArmMir2Lir::OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target) {
OpRegReg(kOpCmp, src1, src2);
return OpCondBranch(cond, target);
}
@@ -123,8 +122,8 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
int32_t val_hi = High32Bits(val);
DCHECK_GE(ModifiedImmediate(val_lo), 0);
DCHECK_GE(ModifiedImmediate(val_hi), 0);
- LIR* taken = &block_label_list_[bb->taken->id];
- LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* taken = &block_label_list_[bb->taken];
+ LIR* not_taken = &block_label_list_[bb->fall_through];
rl_src1 = LoadValueWide(rl_src1, kCoreReg);
int32_t low_reg = rl_src1.low_reg;
int32_t high_reg = rl_src1.high_reg;
@@ -179,23 +178,6 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
RegLocation rl_result;
RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
- // Temporary debugging code
- int dest_sreg = mir->ssa_rep->defs[0];
- if ((dest_sreg < 0) || (dest_sreg >= mir_graph_->GetNumSSARegs())) {
- LOG(INFO) << "Bad target sreg: " << dest_sreg << ", in "
- << PrettyMethod(cu_->method_idx, *cu_->dex_file);
- LOG(INFO) << "at dex offset 0x" << std::hex << mir->offset;
- LOG(INFO) << "vreg = " << mir_graph_->SRegToVReg(dest_sreg);
- LOG(INFO) << "num uses = " << mir->ssa_rep->num_uses;
- if (mir->ssa_rep->num_uses == 1) {
- LOG(INFO) << "CONST case, vals = " << mir->dalvikInsn.vB << ", " << mir->dalvikInsn.vC;
- } else {
- LOG(INFO) << "MOVE case, operands = " << mir->ssa_rep->uses[1] << ", "
- << mir->ssa_rep->uses[2];
- }
- CHECK(false) << "Invalid target sreg on Select.";
- }
- // End temporary debugging code
RegLocation rl_dest = mir_graph_->GetDest(mir);
rl_src = LoadValue(rl_src, kCoreReg);
if (mir->ssa_rep->num_uses == 1) {
@@ -234,11 +216,17 @@ void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
rl_false = LoadValue(rl_false, kCoreReg);
rl_result = EvalLoc(rl_dest, kCoreReg, true);
OpRegImm(kOpCmp, rl_src.low_reg, 0);
- OpIT(kCondEq, "E");
- LIR* l1 = OpRegCopy(rl_result.low_reg, rl_true.low_reg);
- l1->flags.is_nop = false; // Make sure this instruction isn't optimized away
- LIR* l2 = OpRegCopy(rl_result.low_reg, rl_false.low_reg);
- l2->flags.is_nop = false; // Make sure this instruction isn't optimized away
+ if (rl_result.low_reg == rl_true.low_reg) { // Is the "true" case already in place?
+ OpIT(kCondNe, "");
+ OpRegCopy(rl_result.low_reg, rl_false.low_reg);
+ } else if (rl_result.low_reg == rl_false.low_reg) { // False case in place?
+ OpIT(kCondEq, "");
+ OpRegCopy(rl_result.low_reg, rl_true.low_reg);
+ } else { // Normal - select between the two.
+ OpIT(kCondEq, "E");
+ OpRegCopy(rl_result.low_reg, rl_true.low_reg);
+ OpRegCopy(rl_result.low_reg, rl_false.low_reg);
+ }
GenBarrier(); // Add a scheduling barrier to keep the IT shadow intact
}
StoreValue(rl_dest, rl_result);
@@ -265,8 +253,8 @@ void ArmMir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
return;
}
}
- LIR* taken = &block_label_list_[bb->taken->id];
- LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* taken = &block_label_list_[bb->taken];
+ LIR* not_taken = &block_label_list_[bb->fall_through];
rl_src1 = LoadValueWide(rl_src1, kCoreReg);
rl_src2 = LoadValueWide(rl_src2, kCoreReg);
OpRegReg(kOpCmp, rl_src1.high_reg, rl_src2.high_reg);
@@ -313,7 +301,18 @@ LIR* ArmMir2Lir::OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
LIR* branch;
int mod_imm;
ArmConditionCode arm_cond = ArmConditionEncoding(cond);
- if ((ARM_LOWREG(reg)) && (check_value == 0) &&
+ /*
+ * A common use of OpCmpImmBranch is for null checks, and using the Thumb 16-bit
+ * compare-and-branch if zero is ideal if it will reach. However, because null checks
+ * branch forward to a launch pad, they will frequently not reach - and thus have to
+ * be converted to a long form during assembly (which will trigger another assembly
+ * pass). Here we estimate the branch distance for checks, and if large directly
+ * generate the long form in an attempt to avoid an extra assembly pass.
+ * TODO: consider interspersing launchpads in code following unconditional branches.
+ */
+ bool skip = ((target != NULL) && (target->opcode == kPseudoThrowTarget));
+ skip &= ((cu_->code_item->insns_size_in_code_units_ - current_dalvik_offset_) > 64);
+ if (!skip && (ARM_LOWREG(reg)) && (check_value == 0) &&
((arm_cond == kArmCondEq) || (arm_cond == kArmCondNe))) {
branch = NewLIR2((arm_cond == kArmCondEq) ? kThumb2Cbz : kThumb2Cbnz,
reg, 0);
@@ -467,14 +466,39 @@ LIR* ArmMir2Lir::GenRegMemCheck(ConditionCode c_code,
RegLocation ArmMir2Lir::GenDivRemLit(RegLocation rl_dest, int reg1, int lit,
bool is_div) {
- LOG(FATAL) << "Unexpected use of GenDivRemLit for Arm";
- return rl_dest;
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+
+ // Put the literal in a temp.
+ int lit_temp = AllocTemp();
+ LoadConstant(lit_temp, lit);
+ // Use the generic case for div/rem with arg2 in a register.
+ // TODO: The literal temp can be freed earlier during a modulus to reduce reg pressure.
+ rl_result = GenDivRem(rl_result, reg1, lit_temp, is_div);
+ FreeTemp(lit_temp);
+
+ return rl_result;
}
RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, int reg1, int reg2,
bool is_div) {
- LOG(FATAL) << "Unexpected use of GenDivRem for Arm";
- return rl_dest;
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (is_div) {
+ // Simple case, use sdiv instruction.
+ OpRegRegReg(kOpDiv, rl_result.low_reg, reg1, reg2);
+ } else {
+ // Remainder case, use the following code:
+ // temp = reg1 / reg2 - integer division
+ // temp = temp * reg2
+ // dest = reg1 - temp
+
+ int temp = AllocTemp();
+ OpRegRegReg(kOpDiv, temp, reg1, reg2);
+ OpRegReg(kOpMul, temp, reg2);
+ OpRegRegReg(kOpSub, rl_result.low_reg, reg1, temp);
+ FreeTemp(temp);
+ }
+
+ return rl_result;
}
bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
@@ -494,6 +518,50 @@ bool ArmMir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
return true;
}
+bool ArmMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == kLong) {
+ // Fake unaligned LDRD by two unaligned LDR instructions on ARMv7 with SCTLR.A set to 0.
+ if (rl_address.low_reg != rl_result.low_reg) {
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, kWord, INVALID_SREG);
+ LoadBaseDisp(rl_address.low_reg, 4, rl_result.high_reg, kWord, INVALID_SREG);
+ } else {
+ LoadBaseDisp(rl_address.low_reg, 4, rl_result.high_reg, kWord, INVALID_SREG);
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, kWord, INVALID_SREG);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned load with LDR and LDRSH is allowed on ARMv7 with SCTLR.A set to 0.
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
+bool ArmMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_src_value = info->args[2]; // [size] value
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ if (size == kLong) {
+ // Fake unaligned STRD by two unaligned STR instructions on ARMv7 with SCTLR.A set to 0.
+ RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, kWord);
+ StoreBaseDisp(rl_address.low_reg, 4, rl_value.high_reg, kWord);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned store with STR and STRSH is allowed on ARMv7 with SCTLR.A set to 0.
+ RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
+ }
+ return true;
+}
+
void ArmMir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
LOG(FATAL) << "Unexpected use of OpLea for Arm";
}
@@ -618,7 +686,7 @@ void ArmMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind) {
break;
}
LIR* dmb = NewLIR1(kThumb2Dmb, dmb_flavor);
- dmb->def_mask = ENCODE_ALL;
+ dmb->u.m.def_mask = ENCODE_ALL;
#endif
}
@@ -755,7 +823,7 @@ void ArmMir2Lir::GenXorLong(RegLocation rl_dest, RegLocation rl_src1,
* Generate array load
*/
void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale) {
+ RegLocation rl_index, RegLocation rl_dest, int scale) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -845,13 +913,13 @@ void ArmMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
*
*/
void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset;
bool constant_index = rl_index.is_const;
- if (rl_src.wide) {
+ int data_offset;
+ if (size == kLong || size == kDouble) {
data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
} else {
data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
@@ -868,12 +936,14 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
}
int reg_ptr;
+ bool allocated_reg_ptr_temp = false;
if (constant_index) {
reg_ptr = rl_array.low_reg;
- } else if (IsTemp(rl_array.low_reg)) {
+ } else if (IsTemp(rl_array.low_reg) && !card_mark) {
Clobber(rl_array.low_reg);
reg_ptr = rl_array.low_reg;
} else {
+ allocated_reg_ptr_temp = true;
reg_ptr = AllocTemp();
}
@@ -924,71 +994,15 @@ void ArmMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
scale, size);
}
- if (!constant_index) {
+ if (allocated_reg_ptr_temp) {
FreeTemp(reg_ptr);
}
-}
-
-/*
- * Generate array store
- *
- */
-void ArmMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
- int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
-
- FlushAllRegs(); // Use explicit registers
- LockCallTemps();
-
- int r_value = TargetReg(kArg0); // Register holding value
- int r_array_class = TargetReg(kArg1); // Register holding array's Class
- int r_array = TargetReg(kArg2); // Register holding array
- int r_index = TargetReg(kArg3); // Register holding index into array
-
- LoadValueDirectFixed(rl_array, r_array); // Grab array
- LoadValueDirectFixed(rl_src, r_value); // Grab value
- LoadValueDirectFixed(rl_index, r_index); // Grab index
-
- GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
-
- // Store of null?
- LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
-
- // Get the array's class.
- LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
- r_array_class, true);
- // Redo LoadValues in case they didn't survive the call.
- LoadValueDirectFixed(rl_array, r_array); // Reload array
- LoadValueDirectFixed(rl_index, r_index); // Reload index
- LoadValueDirectFixed(rl_src, r_value); // Reload value
- r_array_class = INVALID_REG;
-
- // Branch here if value to be stored == null
- LIR* target = NewLIR0(kPseudoTargetLabel);
- null_value_check->target = target;
-
- bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
- int reg_len = INVALID_REG;
- if (needs_range_check) {
- reg_len = TargetReg(kArg1);
- LoadWordDisp(r_array, len_offset, reg_len); // Get len
- }
- /* r_ptr -> array data */
- int r_ptr = AllocTemp();
- OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
- if (needs_range_check) {
- GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
- }
- StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
- FreeTemp(r_ptr);
- FreeTemp(r_index);
- if (!mir_graph_->IsConstantNullRef(rl_src)) {
- MarkGCCard(r_value, r_array);
+ if (card_mark) {
+ MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
+
void ArmMir2Lir::GenShiftImmOpLong(Instruction::Code opcode,
RegLocation rl_dest, RegLocation rl_src, RegLocation rl_shift) {
rl_src = LoadValueWide(rl_src, kCoreReg);
diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc
index 6cc3052da1..52aba9b4df 100644
--- a/compiler/dex/quick/arm/target_arm.cc
+++ b/compiler/dex/quick/arm/target_arm.cc
@@ -74,6 +74,8 @@ int ArmMir2Lir::TargetReg(SpecialTargetRegister reg) {
case kRet0: res = rARM_RET0; break;
case kRet1: res = rARM_RET1; break;
case kInvokeTgt: res = rARM_INVOKE_TGT; break;
+ case kHiddenArg: res = r12; break;
+ case kHiddenFpArg: res = INVALID_REG; break;
case kCount: res = rARM_COUNT; break;
}
return res;
@@ -118,78 +120,83 @@ uint64_t ArmMir2Lir::GetPCUseDefEncoding() {
return ENCODE_ARM_REG_PC;
}
-void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir) {
+// Thumb2 specific setup. TODO: inline?:
+void ArmMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) {
DCHECK_EQ(cu_->instruction_set, kThumb2);
+ DCHECK(!lir->flags.use_def_invalid);
- // Thumb2 specific setup
- uint64_t flags = ArmMir2Lir::EncodingMap[lir->opcode].flags;
int opcode = lir->opcode;
- if (flags & REG_DEF_SP) {
- lir->def_mask |= ENCODE_ARM_REG_SP;
- }
+ // These flags are somewhat uncommon - bypass if we can.
+ if ((flags & (REG_DEF_SP | REG_USE_SP | REG_DEF_LIST0 | REG_DEF_LIST1 |
+ REG_DEF_FPCS_LIST0 | REG_DEF_FPCS_LIST2 | REG_USE_PC | IS_IT | REG_USE_LIST0 |
+ REG_USE_LIST1 | REG_USE_FPCS_LIST0 | REG_USE_FPCS_LIST2 | REG_DEF_LR)) != 0) {
+ if (flags & REG_DEF_SP) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_SP;
+ }
- if (flags & REG_USE_SP) {
- lir->use_mask |= ENCODE_ARM_REG_SP;
- }
+ if (flags & REG_USE_SP) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_SP;
+ }
- if (flags & REG_DEF_LIST0) {
- lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
- }
+ if (flags & REG_DEF_LIST0) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
- if (flags & REG_DEF_LIST1) {
- lir->def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
- }
+ if (flags & REG_DEF_LIST1) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
- if (flags & REG_DEF_FPCS_LIST0) {
- lir->def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
- }
+ if (flags & REG_DEF_FPCS_LIST0) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
- if (flags & REG_DEF_FPCS_LIST2) {
- for (int i = 0; i < lir->operands[2]; i++) {
- SetupRegMask(&lir->def_mask, lir->operands[1] + i);
+ if (flags & REG_DEF_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->u.m.def_mask, lir->operands[1] + i);
+ }
}
- }
- if (flags & REG_USE_PC) {
- lir->use_mask |= ENCODE_ARM_REG_PC;
- }
+ if (flags & REG_USE_PC) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_PC;
+ }
- /* Conservatively treat the IT block */
- if (flags & IS_IT) {
- lir->def_mask = ENCODE_ALL;
- }
+ /* Conservatively treat the IT block */
+ if (flags & IS_IT) {
+ lir->u.m.def_mask = ENCODE_ALL;
+ }
- if (flags & REG_USE_LIST0) {
- lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
- }
+ if (flags & REG_USE_LIST0) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_LIST(lir->operands[0]);
+ }
- if (flags & REG_USE_LIST1) {
- lir->use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
- }
+ if (flags & REG_USE_LIST1) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_LIST(lir->operands[1]);
+ }
- if (flags & REG_USE_FPCS_LIST0) {
- lir->use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
- }
+ if (flags & REG_USE_FPCS_LIST0) {
+ lir->u.m.use_mask |= ENCODE_ARM_REG_FPCS_LIST(lir->operands[0]);
+ }
- if (flags & REG_USE_FPCS_LIST2) {
- for (int i = 0; i < lir->operands[2]; i++) {
- SetupRegMask(&lir->use_mask, lir->operands[1] + i);
+ if (flags & REG_USE_FPCS_LIST2) {
+ for (int i = 0; i < lir->operands[2]; i++) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[1] + i);
+ }
}
- }
- /* Fixup for kThumbPush/lr and kThumbPop/pc */
- if (opcode == kThumbPush || opcode == kThumbPop) {
- uint64_t r8Mask = GetRegMaskCommon(r8);
- if ((opcode == kThumbPush) && (lir->use_mask & r8Mask)) {
- lir->use_mask &= ~r8Mask;
- lir->use_mask |= ENCODE_ARM_REG_LR;
- } else if ((opcode == kThumbPop) && (lir->def_mask & r8Mask)) {
- lir->def_mask &= ~r8Mask;
- lir->def_mask |= ENCODE_ARM_REG_PC;
+ /* Fixup for kThumbPush/lr and kThumbPop/pc */
+ if (opcode == kThumbPush || opcode == kThumbPop) {
+ uint64_t r8Mask = GetRegMaskCommon(r8);
+ if ((opcode == kThumbPush) && (lir->u.m.use_mask & r8Mask)) {
+ lir->u.m.use_mask &= ~r8Mask;
+ lir->u.m.use_mask |= ENCODE_ARM_REG_LR;
+ } else if ((opcode == kThumbPop) && (lir->u.m.def_mask & r8Mask)) {
+ lir->u.m.def_mask &= ~r8Mask;
+ lir->u.m.def_mask |= ENCODE_ARM_REG_PC;
+ }
+ }
+ if (flags & REG_DEF_LR) {
+ lir->u.m.def_mask |= ENCODE_ARM_REG_LR;
}
- }
- if (flags & REG_DEF_LR) {
- lir->def_mask |= ENCODE_ARM_REG_LR;
}
}
@@ -277,8 +284,8 @@ static char* DecodeFPCSRegList(int count, int base, char* buf) {
return buf;
}
-static int ExpandImmediate(int value) {
- int mode = (value & 0xf00) >> 8;
+static int32_t ExpandImmediate(int value) {
+ int32_t mode = (value & 0xf00) >> 8;
uint32_t bits = value & 0xff;
switch (mode) {
case 0:
@@ -466,8 +473,8 @@ void ArmMir2Lir::DumpResourceMask(LIR* arm_lir, uint64_t mask, const char* prefi
/* Memory bits */
if (arm_lir && (mask & ENCODE_DALVIK_REG)) {
- sprintf(buf + strlen(buf), "dr%d%s", arm_lir->alias_info & 0xffff,
- (arm_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(arm_lir->flags.alias_info),
+ DECODE_ALIAS_INFO_WIDE(arm_lir->flags.alias_info) ? "(+1)" : "");
}
if (mask & ENCODE_LITERAL) {
strcat(buf, "lit ");
@@ -691,11 +698,6 @@ RegLocation ArmMir2Lir::GetReturnAlt() {
return res;
}
-ArmMir2Lir::RegisterInfo* ArmMir2Lir::GetRegInfo(int reg) {
- return ARM_FPREG(reg) ? &reg_pool_->FPRegs[reg & ARM_FP_REG_MASK]
- : &reg_pool_->core_regs[reg];
-}
-
/* To be used when explicitly managing register use */
void ArmMir2Lir::LockCallTemps() {
LockTemp(r0);
@@ -718,14 +720,17 @@ int ArmMir2Lir::LoadHelper(ThreadOffset offset) {
}
uint64_t ArmMir2Lir::GetTargetInstFlags(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return ArmMir2Lir::EncodingMap[opcode].flags;
}
const char* ArmMir2Lir::GetTargetInstName(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return ArmMir2Lir::EncodingMap[opcode].name;
}
const char* ArmMir2Lir::GetTargetInstFmt(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return ArmMir2Lir::EncodingMap[opcode].fmt;
}
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index c63de69284..d631cf7047 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -22,14 +22,14 @@ namespace art {
/* This file contains codegen for the Thumb ISA. */
-static int EncodeImmSingle(int value) {
- int res;
- int bit_a = (value & 0x80000000) >> 31;
- int not_bit_b = (value & 0x40000000) >> 30;
- int bit_b = (value & 0x20000000) >> 29;
- int b_smear = (value & 0x3e000000) >> 25;
- int slice = (value & 0x01f80000) >> 19;
- int zeroes = (value & 0x0007ffff);
+static int32_t EncodeImmSingle(int32_t value) {
+ int32_t res;
+ int32_t bit_a = (value & 0x80000000) >> 31;
+ int32_t not_bit_b = (value & 0x40000000) >> 30;
+ int32_t bit_b = (value & 0x20000000) >> 29;
+ int32_t b_smear = (value & 0x3e000000) >> 25;
+ int32_t slice = (value & 0x01f80000) >> 19;
+ int32_t zeroes = (value & 0x0007ffff);
if (zeroes != 0)
return -1;
if (bit_b) {
@@ -47,15 +47,15 @@ static int EncodeImmSingle(int value) {
* Determine whether value can be encoded as a Thumb2 floating point
* immediate. If not, return -1. If so return encoded 8-bit value.
*/
-static int EncodeImmDouble(int64_t value) {
- int res;
- int bit_a = (value & 0x8000000000000000ll) >> 63;
- int not_bit_b = (value & 0x4000000000000000ll) >> 62;
- int bit_b = (value & 0x2000000000000000ll) >> 61;
- int b_smear = (value & 0x3fc0000000000000ll) >> 54;
- int slice = (value & 0x003f000000000000ll) >> 48;
+static int32_t EncodeImmDouble(int64_t value) {
+ int32_t res;
+ int32_t bit_a = (value & 0x8000000000000000ll) >> 63;
+ int32_t not_bit_b = (value & 0x4000000000000000ll) >> 62;
+ int32_t bit_b = (value & 0x2000000000000000ll) >> 61;
+ int32_t b_smear = (value & 0x3fc0000000000000ll) >> 54;
+ int32_t slice = (value & 0x003f000000000000ll) >> 48;
uint64_t zeroes = (value & 0x0000ffffffffffffll);
- if (zeroes != 0)
+ if (zeroes != 0ull)
return -1;
if (bit_b) {
if ((not_bit_b != 0) || (b_smear != 0xff))
@@ -90,15 +90,14 @@ LIR* ArmMir2Lir::LoadFPConstantValue(int r_dest, int value) {
LIR* load_pc_rel = RawLIR(current_dalvik_offset_, kThumb2Vldrs,
r_dest, r15pc, 0, 0, 0, data_target);
SetMemRefType(load_pc_rel, true, kLiteral);
- load_pc_rel->alias_info = reinterpret_cast<uintptr_t>(data_target);
AppendLIR(load_pc_rel);
return load_pc_rel;
}
static int LeadingZeros(uint32_t val) {
uint32_t alt;
- int n;
- int count;
+ int32_t n;
+ int32_t count;
count = 16;
n = 32;
@@ -118,8 +117,8 @@ static int LeadingZeros(uint32_t val) {
* immediate. If not, return -1. If so, return i:imm3:a:bcdefgh form.
*/
int ArmMir2Lir::ModifiedImmediate(uint32_t value) {
- int z_leading;
- int z_trailing;
+ int32_t z_leading;
+ int32_t z_trailing;
uint32_t b0 = value & 0xff;
/* Note: case of value==0 must use 0:000:0:0000000 encoding */
@@ -315,6 +314,22 @@ LIR* ArmMir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2,
case kOpSub:
opcode = (thumb_form) ? kThumbSubRRR : kThumb2SubRRR;
break;
+ case kOpRev:
+ DCHECK_EQ(shift, 0);
+ if (!thumb_form) {
+ // Binary, but rm is encoded twice.
+ return NewLIR3(kThumb2RevRR, r_dest_src1, r_src2, r_src2);
+ }
+ opcode = kThumbRev;
+ break;
+ case kOpRevsh:
+ DCHECK_EQ(shift, 0);
+ if (!thumb_form) {
+ // Binary, but rm is encoded twice.
+ return NewLIR3(kThumb2RevshRR, r_dest_src1, r_src2, r_src2);
+ }
+ opcode = kThumbRevsh;
+ break;
case kOp2Byte:
DCHECK_EQ(shift, 0);
return NewLIR4(kThumb2Sbfx, r_dest_src1, r_src2, 0, 8);
@@ -328,7 +343,7 @@ LIR* ArmMir2Lir::OpRegRegShift(OpKind op, int r_dest_src1, int r_src2,
LOG(FATAL) << "Bad opcode: " << op;
break;
}
- DCHECK_GE(static_cast<int>(opcode), 0);
+ DCHECK(!IsPseudoLirOp(opcode));
if (EncodingMap[opcode].flags & IS_BINARY_OP) {
return NewLIR2(opcode, r_dest_src1, r_src2);
} else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) {
@@ -380,6 +395,10 @@ LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1,
DCHECK_EQ(shift, 0);
opcode = kThumb2MulRRR;
break;
+ case kOpDiv:
+ DCHECK_EQ(shift, 0);
+ opcode = kThumb2SdivRRR;
+ break;
case kOpOr:
opcode = kThumb2OrrRRR;
break;
@@ -406,7 +425,7 @@ LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, int r_dest, int r_src1,
LOG(FATAL) << "Bad opcode: " << op;
break;
}
- DCHECK_GE(static_cast<int>(opcode), 0);
+ DCHECK(!IsPseudoLirOp(opcode));
if (EncodingMap[opcode].flags & IS_QUAD_OP) {
return NewLIR4(opcode, r_dest, r_src1, r_src2, shift);
} else {
@@ -422,12 +441,12 @@ LIR* ArmMir2Lir::OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) {
LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) {
LIR* res;
bool neg = (value < 0);
- int abs_value = (neg) ? -value : value;
+ int32_t abs_value = (neg) ? -value : value;
ArmOpcode opcode = kThumbBkpt;
ArmOpcode alt_opcode = kThumbBkpt;
bool all_low_regs = (ARM_LOWREG(r_dest) && ARM_LOWREG(r_src1));
- int mod_imm = ModifiedImmediate(value);
- int mod_imm_neg = ModifiedImmediate(-value);
+ int32_t mod_imm = ModifiedImmediate(value);
+ int32_t mod_imm_neg = ModifiedImmediate(-value);
switch (op) {
case kOpLsl:
@@ -545,7 +564,7 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) {
/* Handle Thumb-only variants here - otherwise punt to OpRegRegImm */
LIR* ArmMir2Lir::OpRegImm(OpKind op, int r_dest_src1, int value) {
bool neg = (value < 0);
- int abs_value = (neg) ? -value : value;
+ int32_t abs_value = (neg) ? -value : value;
bool short_form = (((abs_value & 0xff) == abs_value) && ARM_LOWREG(r_dest_src1));
ArmOpcode opcode = kThumbBkpt;
switch (op) {
@@ -626,7 +645,6 @@ LIR* ArmMir2Lir::LoadConstantWide(int r_dest_lo, int r_dest_hi, int64_t value) {
r_dest_lo, r_dest_hi, r15pc, 0, 0, data_target);
}
SetMemRefType(res, true, kLiteral);
- res->alias_info = reinterpret_cast<uintptr_t>(data_target);
AppendLIR(res);
}
return res;
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index a49fa7b44d..dfbc887299 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -45,29 +45,54 @@ bool Mir2Lir::IsInexpensiveConstant(RegLocation rl_src) {
}
void Mir2Lir::MarkSafepointPC(LIR* inst) {
- inst->def_mask = ENCODE_ALL;
+ DCHECK(!inst->flags.use_def_invalid);
+ inst->u.m.def_mask = ENCODE_ALL;
LIR* safepoint_pc = NewLIR0(kPseudoSafepointPC);
- DCHECK_EQ(safepoint_pc->def_mask, ENCODE_ALL);
+ DCHECK_EQ(safepoint_pc->u.m.def_mask, ENCODE_ALL);
}
-bool Mir2Lir::FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put) {
+bool Mir2Lir::FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile) {
return cu_->compiler_driver->ComputeInstanceFieldInfo(
- field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, is_volatile, is_put);
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), is_put, field_offset, is_volatile);
+}
+
+/* Remove a LIR from the list. */
+void Mir2Lir::UnlinkLIR(LIR* lir) {
+ if (UNLIKELY(lir == first_lir_insn_)) {
+ first_lir_insn_ = lir->next;
+ if (lir->next != NULL) {
+ lir->next->prev = NULL;
+ } else {
+ DCHECK(lir->next == NULL);
+ DCHECK(lir == last_lir_insn_);
+ last_lir_insn_ = NULL;
+ }
+ } else if (lir == last_lir_insn_) {
+ last_lir_insn_ = lir->prev;
+ lir->prev->next = NULL;
+ } else if ((lir->prev != NULL) && (lir->next != NULL)) {
+ lir->prev->next = lir->next;
+ lir->next->prev = lir->prev;
+ }
}
/* Convert an instruction to a NOP */
void Mir2Lir::NopLIR(LIR* lir) {
lir->flags.is_nop = true;
+ if (!cu_->verbose) {
+ UnlinkLIR(lir);
+ }
}
void Mir2Lir::SetMemRefType(LIR* lir, bool is_load, int mem_type) {
uint64_t *mask_ptr;
uint64_t mask = ENCODE_MEM;
DCHECK(GetTargetInstFlags(lir->opcode) & (IS_LOAD | IS_STORE));
+ DCHECK(!lir->flags.use_def_invalid);
if (is_load) {
- mask_ptr = &lir->use_mask;
+ mask_ptr = &lir->u.m.use_mask;
} else {
- mask_ptr = &lir->def_mask;
+ mask_ptr = &lir->u.m.def_mask;
}
/* Clear out the memref flags */
*mask_ptr &= ~mask;
@@ -104,7 +129,7 @@ void Mir2Lir::AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load,
* Store the Dalvik register id in alias_info. Mark the MSB if it is a 64-bit
* access.
*/
- lir->alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit);
+ lir->flags.alias_info = ENCODE_ALIAS_INFO(reg_id, is64bit);
}
/*
@@ -135,10 +160,12 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) {
break;
case kPseudoDalvikByteCodeBoundary:
if (lir->operands[0] == 0) {
- lir->operands[0] = reinterpret_cast<uintptr_t>("No instruction string");
+ // NOTE: only used for debug listings.
+ lir->operands[0] = WrapPointer(ArenaStrdup("No instruction string"));
}
LOG(INFO) << "-------- dalvik offset: 0x" << std::hex
- << lir->dalvik_offset << " @ " << reinterpret_cast<char*>(lir->operands[0]);
+ << lir->dalvik_offset << " @ "
+ << reinterpret_cast<char*>(UnwrapPointer(lir->operands[0]));
break;
case kPseudoExitBlock:
LOG(INFO) << "-------- exit offset: 0x" << std::hex << dest;
@@ -190,11 +217,11 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) {
break;
}
- if (lir->use_mask && (!lir->flags.is_nop || dump_nop)) {
- DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->use_mask, "use"));
+ if (lir->u.m.use_mask && (!lir->flags.is_nop || dump_nop)) {
+ DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->u.m.use_mask, "use"));
}
- if (lir->def_mask && (!lir->flags.is_nop || dump_nop)) {
- DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->def_mask, "def"));
+ if (lir->u.m.def_mask && (!lir->flags.is_nop || dump_nop)) {
+ DUMP_RESOURCE_MASK(DumpResourceMask(lir, lir->u.m.def_mask, "def"));
}
}
@@ -225,12 +252,12 @@ void Mir2Lir::DumpPromotionMap() {
}
/* Dump a mapping table */
-void Mir2Lir::DumpMappingTable(const char* table_name, const std::string& descriptor,
- const std::string& name, const std::string& signature,
+void Mir2Lir::DumpMappingTable(const char* table_name, const char* descriptor,
+ const char* name, const Signature& signature,
const std::vector<uint32_t>& v) {
if (v.size() > 0) {
std::string line(StringPrintf("\n %s %s%s_%s_table[%zu] = {", table_name,
- descriptor.c_str(), name.c_str(), signature.c_str(), v.size()));
+ descriptor, name, signature.ToString().c_str(), v.size()));
std::replace(line.begin(), line.end(), ';', '_');
LOG(INFO) << line;
for (uint32_t i = 0; i < v.size(); i+=2) {
@@ -270,9 +297,9 @@ void Mir2Lir::CodegenDump() {
const DexFile::MethodId& method_id =
cu_->dex_file->GetMethodId(cu_->method_idx);
- std::string signature(cu_->dex_file->GetMethodSignature(method_id));
- std::string name(cu_->dex_file->GetMethodName(method_id));
- std::string descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
+ const Signature signature = cu_->dex_file->GetMethodSignature(method_id);
+ const char* name = cu_->dex_file->GetMethodName(method_id);
+ const char* descriptor(cu_->dex_file->GetMethodDeclaringClassDescriptor(method_id));
// Dump mapping tables
DumpMappingTable("PC2Dex_MappingTable", descriptor, name, signature, pc2dex_mapping_table_);
@@ -325,6 +352,7 @@ LIR* Mir2Lir::AddWordData(LIR* *constant_list_p, int value) {
new_value->operands[0] = value;
new_value->next = *constant_list_p;
*constant_list_p = new_value;
+ estimated_native_code_size_ += sizeof(value);
return new_value;
}
return NULL;
@@ -343,6 +371,17 @@ static void PushWord(std::vector<uint8_t>&buf, int data) {
buf.push_back((data >> 24) & 0xff);
}
+// Push 8 bytes on 64-bit systems; 4 on 32-bit systems.
+static void PushPointer(std::vector<uint8_t>&buf, void const* pointer) {
+ uintptr_t data = reinterpret_cast<uintptr_t>(pointer);
+ if (sizeof(void*) == sizeof(uint64_t)) {
+ PushWord(buf, (data >> (sizeof(void*) * 4)) & 0xFFFFFFFF);
+ PushWord(buf, data & 0xFFFFFFFF);
+ } else {
+ PushWord(buf, data);
+ }
+}
+
static void AlignBuffer(std::vector<uint8_t>&buf, size_t offset) {
while (buf.size() < offset) {
buf.push_back(0);
@@ -369,9 +408,8 @@ void Mir2Lir::InstallLiteralPools() {
static_cast<InvokeType>(data_lir->operands[1]),
code_buffer_.size());
const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
- // unique based on target to ensure code deduplication works
- uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
- PushWord(code_buffer_, unique_patch_value);
+ // unique value based on target to ensure code deduplication works
+ PushPointer(code_buffer_, &id);
data_lir = NEXT_LIR(data_lir);
}
data_lir = method_literal_list_;
@@ -385,9 +423,8 @@ void Mir2Lir::InstallLiteralPools() {
static_cast<InvokeType>(data_lir->operands[1]),
code_buffer_.size());
const DexFile::MethodId& id = cu_->dex_file->GetMethodId(target);
- // unique based on target to ensure code deduplication works
- uint32_t unique_patch_value = reinterpret_cast<uint32_t>(&id);
- PushWord(code_buffer_, unique_patch_value);
+ // unique value based on target to ensure code deduplication works
+ PushPointer(code_buffer_, &id);
data_lir = NEXT_LIR(data_lir);
}
}
@@ -408,6 +445,7 @@ void Mir2Lir::InstallSwitchTables() {
int bx_offset = INVALID_OFFSET;
switch (cu_->instruction_set) {
case kThumb2:
+ DCHECK(tab_rec->anchor->flags.fixup != kFixupNone);
bx_offset = tab_rec->anchor->offset + 4;
break;
case kX86:
@@ -422,7 +460,7 @@ void Mir2Lir::InstallSwitchTables() {
LOG(INFO) << "Switch table for offset 0x" << std::hex << bx_offset;
}
if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
- const int* keys = reinterpret_cast<const int*>(&(tab_rec->table[2]));
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&(tab_rec->table[2]));
for (int elems = 0; elems < tab_rec->table[1]; elems++) {
int disp = tab_rec->targets[elems]->offset - bx_offset;
if (cu_->verbose) {
@@ -463,7 +501,7 @@ void Mir2Lir::InstallFillArrayData() {
}
}
-static int AssignLiteralOffsetCommon(LIR* lir, int offset) {
+static int AssignLiteralOffsetCommon(LIR* lir, CodeOffset offset) {
for (; lir != NULL; lir = lir->next) {
lir->offset = offset;
offset += 4;
@@ -471,6 +509,17 @@ static int AssignLiteralOffsetCommon(LIR* lir, int offset) {
return offset;
}
+static int AssignLiteralPointerOffsetCommon(LIR* lir, CodeOffset offset) {
+ unsigned int element_size = sizeof(void*);
+ // Align to natural pointer size.
+ offset = (offset + (element_size - 1)) & ~(element_size - 1);
+ for (; lir != NULL; lir = lir->next) {
+ lir->offset = offset;
+ offset += element_size;
+ }
+ return offset;
+}
+
// Make sure we have a code address for every declared catch entry
bool Mir2Lir::VerifyCatchEntries() {
bool success = true;
@@ -580,8 +629,8 @@ class NativePcToReferenceMapBuilder {
table_index = (table_index + 1) % entries_;
}
in_use_[table_index] = true;
- SetNativeOffset(table_index, native_offset);
- DCHECK_EQ(native_offset, GetNativeOffset(table_index));
+ SetCodeOffset(table_index, native_offset);
+ DCHECK_EQ(native_offset, GetCodeOffset(table_index));
SetReferences(table_index, references);
}
@@ -590,7 +639,7 @@ class NativePcToReferenceMapBuilder {
return NativePcOffsetToReferenceMap::Hash(native_offset) % entries_;
}
- uint32_t GetNativeOffset(size_t table_index) {
+ uint32_t GetCodeOffset(size_t table_index) {
uint32_t native_offset = 0;
size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
for (size_t i = 0; i < native_offset_width_; i++) {
@@ -599,7 +648,7 @@ class NativePcToReferenceMapBuilder {
return native_offset;
}
- void SetNativeOffset(size_t table_index, uint32_t native_offset) {
+ void SetCodeOffset(size_t table_index, uint32_t native_offset) {
size_t table_offset = (table_index * EntryWidth()) + sizeof(uint32_t);
for (size_t i = 0; i < native_offset_width_; i++) {
(*table_)[table_offset + i] = (native_offset >> (i * 8)) & 0xFF;
@@ -654,17 +703,17 @@ void Mir2Lir::CreateNativeGcMap() {
}
/* Determine the offset of each literal field */
-int Mir2Lir::AssignLiteralOffset(int offset) {
+int Mir2Lir::AssignLiteralOffset(CodeOffset offset) {
offset = AssignLiteralOffsetCommon(literal_list_, offset);
- offset = AssignLiteralOffsetCommon(code_literal_list_, offset);
- offset = AssignLiteralOffsetCommon(method_literal_list_, offset);
+ offset = AssignLiteralPointerOffsetCommon(code_literal_list_, offset);
+ offset = AssignLiteralPointerOffsetCommon(method_literal_list_, offset);
return offset;
}
-int Mir2Lir::AssignSwitchTablesOffset(int offset) {
+int Mir2Lir::AssignSwitchTablesOffset(CodeOffset offset) {
GrowableArray<SwitchTable*>::Iterator iterator(&switch_tables_);
while (true) {
- Mir2Lir::SwitchTable *tab_rec = iterator.Next();
+ Mir2Lir::SwitchTable* tab_rec = iterator.Next();
if (tab_rec == NULL) break;
tab_rec->offset = offset;
if (tab_rec->table[0] == Instruction::kSparseSwitchSignature) {
@@ -678,7 +727,7 @@ int Mir2Lir::AssignSwitchTablesOffset(int offset) {
return offset;
}
-int Mir2Lir::AssignFillArrayDataOffset(int offset) {
+int Mir2Lir::AssignFillArrayDataOffset(CodeOffset offset) {
GrowableArray<FillArrayData*>::Iterator iterator(&fill_array_data_);
while (true) {
Mir2Lir::FillArrayData *tab_rec = iterator.Next();
@@ -691,122 +740,35 @@ int Mir2Lir::AssignFillArrayDataOffset(int offset) {
return offset;
}
-// LIR offset assignment.
-int Mir2Lir::AssignInsnOffsets() {
- LIR* lir;
- int offset = 0;
-
- for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
- lir->offset = offset;
- if (lir->opcode >= 0) {
- if (!lir->flags.is_nop) {
- offset += lir->flags.size;
- }
- } else if (lir->opcode == kPseudoPseudoAlign4) {
- if (offset & 0x2) {
- offset += 2;
- lir->operands[0] = 1;
- } else {
- lir->operands[0] = 0;
- }
- }
- /* Pseudo opcodes don't consume space */
- }
-
- return offset;
-}
-
-/*
- * Walk the compilation unit and assign offsets to instructions
- * and literals and compute the total size of the compiled unit.
- */
-void Mir2Lir::AssignOffsets() {
- int offset = AssignInsnOffsets();
-
- /* Const values have to be word aligned */
- offset = (offset + 3) & ~3;
-
- /* Set up offsets for literals */
- data_offset_ = offset;
-
- offset = AssignLiteralOffset(offset);
-
- offset = AssignSwitchTablesOffset(offset);
-
- offset = AssignFillArrayDataOffset(offset);
-
- total_size_ = offset;
-}
-
-/*
- * Go over each instruction in the list and calculate the offset from the top
- * before sending them off to the assembler. If out-of-range branch distance is
- * seen rearrange the instructions a bit to correct it.
- */
-void Mir2Lir::AssembleLIR() {
- AssignOffsets();
- int assembler_retries = 0;
- /*
- * Assemble here. Note that we generate code with optimistic assumptions
- * and if found now to work, we'll have to redo the sequence and retry.
- */
-
- while (true) {
- AssemblerStatus res = AssembleInstructions(0);
- if (res == kSuccess) {
- break;
- } else {
- assembler_retries++;
- if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
- CodegenDump();
- LOG(FATAL) << "Assembler error - too many retries";
- }
- // Redo offsets and try again
- AssignOffsets();
- code_buffer_.clear();
- }
- }
-
- // Install literals
- InstallLiteralPools();
-
- // Install switch tables
- InstallSwitchTables();
-
- // Install fill array data
- InstallFillArrayData();
-
- // Create the mapping table and native offset to reference map.
- CreateMappingTables();
-
- CreateNativeGcMap();
-}
-
/*
* Insert a kPseudoCaseLabel at the beginning of the Dalvik
- * offset vaddr. This label will be used to fix up the case
- * branch table during the assembly phase. Be sure to set
- * all resource flags on this to prevent code motion across
- * target boundaries. KeyVal is just there for debugging.
+ * offset vaddr if pretty-printing, otherise use the standard block
+ * label. The selected label will be used to fix up the case
+ * branch table during the assembly phase. All resource flags
+ * are set to prevent code motion. KeyVal is just there for debugging.
*/
-LIR* Mir2Lir::InsertCaseLabel(int vaddr, int keyVal) {
- SafeMap<unsigned int, LIR*>::iterator it;
- it = boundary_map_.find(vaddr);
- if (it == boundary_map_.end()) {
- LOG(FATAL) << "Error: didn't find vaddr 0x" << std::hex << vaddr;
+LIR* Mir2Lir::InsertCaseLabel(DexOffset vaddr, int keyVal) {
+ LIR* boundary_lir = &block_label_list_[mir_graph_->FindBlock(vaddr)->id];
+ LIR* res = boundary_lir;
+ if (cu_->verbose) {
+ // Only pay the expense if we're pretty-printing.
+ LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
+ new_label->dalvik_offset = vaddr;
+ new_label->opcode = kPseudoCaseLabel;
+ new_label->operands[0] = keyVal;
+ new_label->flags.fixup = kFixupLabel;
+ DCHECK(!new_label->flags.use_def_invalid);
+ new_label->u.m.def_mask = ENCODE_ALL;
+ InsertLIRAfter(boundary_lir, new_label);
+ res = new_label;
}
- LIR* new_label = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
- new_label->dalvik_offset = vaddr;
- new_label->opcode = kPseudoCaseLabel;
- new_label->operands[0] = keyVal;
- InsertLIRAfter(it->second, new_label);
- return new_label;
+ return res;
}
-void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec) {
+void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
const uint16_t* table = tab_rec->table;
- int base_vaddr = tab_rec->vaddr;
- const int *targets = reinterpret_cast<const int*>(&table[4]);
+ DexOffset base_vaddr = tab_rec->vaddr;
+ const int32_t *targets = reinterpret_cast<const int32_t*>(&table[4]);
int entries = table[1];
int low_key = s4FromSwitchData(&table[2]);
for (int i = 0; i < entries; i++) {
@@ -814,12 +776,12 @@ void Mir2Lir::MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec) {
}
}
-void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec) {
+void Mir2Lir::MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec) {
const uint16_t* table = tab_rec->table;
- int base_vaddr = tab_rec->vaddr;
+ DexOffset base_vaddr = tab_rec->vaddr;
int entries = table[1];
- const int* keys = reinterpret_cast<const int*>(&table[2]);
- const int* targets = &keys[entries];
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
+ const int32_t* targets = &keys[entries];
for (int i = 0; i < entries; i++) {
tab_rec->targets[i] = InsertCaseLabel(base_vaddr + targets[i], keys[i]);
}
@@ -852,8 +814,8 @@ void Mir2Lir::DumpSparseSwitchTable(const uint16_t* table) {
*/
uint16_t ident = table[0];
int entries = table[1];
- const int* keys = reinterpret_cast<const int*>(&table[2]);
- const int* targets = &keys[entries];
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
+ const int32_t* targets = &keys[entries];
LOG(INFO) << "Sparse switch table - ident:0x" << std::hex << ident
<< ", entries: " << std::dec << entries;
for (int i = 0; i < entries; i++) {
@@ -872,7 +834,7 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) {
* Total size is (4+size*2) 16-bit code units.
*/
uint16_t ident = table[0];
- const int* targets = reinterpret_cast<const int*>(&table[4]);
+ const int32_t* targets = reinterpret_cast<const int32_t*>(&table[4]);
int entries = table[1];
int low_key = s4FromSwitchData(&table[2]);
LOG(INFO) << "Packed switch table - ident:0x" << std::hex << ident
@@ -883,18 +845,10 @@ void Mir2Lir::DumpPackedSwitchTable(const uint16_t* table) {
}
}
-/*
- * Set up special LIR to mark a Dalvik byte-code instruction start and
- * record it in the boundary_map. NOTE: in cases such as kMirOpCheck in
- * which we split a single Dalvik instruction, only the first MIR op
- * associated with a Dalvik PC should be entered into the map.
- */
-LIR* Mir2Lir::MarkBoundary(int offset, const char* inst_str) {
- LIR* res = NewLIR1(kPseudoDalvikByteCodeBoundary, reinterpret_cast<uintptr_t>(inst_str));
- if (boundary_map_.find(offset) == boundary_map_.end()) {
- boundary_map_.Put(offset, res);
- }
- return res;
+/* Set up special LIR to mark a Dalvik byte-code instruction start for pretty printing */
+void Mir2Lir::MarkBoundary(DexOffset offset, const char* inst_str) {
+ // NOTE: only used for debug listings.
+ NewLIR1(kPseudoDalvikByteCodeBoundary, WrapPointer(ArenaStrdup(inst_str)));
}
bool Mir2Lir::EvaluateBranch(Instruction::Code opcode, int32_t src1, int32_t src2) {
@@ -942,6 +896,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena
literal_list_(NULL),
method_literal_list_(NULL),
code_literal_list_(NULL),
+ first_fixup_(NULL),
cu_(cu),
mir_graph_(mir_graph),
switch_tables_(arena, 4, kGrowableArraySwitchTables),
@@ -949,10 +904,14 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena
throw_launchpads_(arena, 2048, kGrowableArrayThrowLaunchPads),
suspend_launchpads_(arena, 4, kGrowableArraySuspendLaunchPads),
intrinsic_launchpads_(arena, 2048, kGrowableArrayMisc),
+ tempreg_info_(arena, 20, kGrowableArrayMisc),
+ reginfo_map_(arena, 64, kGrowableArrayMisc),
+ pointer_storage_(arena, 128, kGrowableArrayMisc),
data_offset_(0),
total_size_(0),
block_label_list_(NULL),
current_dalvik_offset_(0),
+ estimated_native_code_size_(0),
reg_pool_(NULL),
live_sreg_(0),
num_core_spills_(0),
@@ -965,9 +924,13 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena
promotion_map_ = static_cast<PromotionMap*>
(arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) *
sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc));
+ // Reserve pointer id 0 for NULL.
+ size_t null_idx = WrapPointer(NULL);
+ DCHECK_EQ(null_idx, 0U);
}
void Mir2Lir::Materialize() {
+ cu_->NewTimingSplit("RegisterAllocation");
CompilerInitializeRegAlloc(); // Needs to happen after SSA naming
/* Allocate Registers using simple local allocation scheme */
@@ -979,6 +942,7 @@ void Mir2Lir::Materialize() {
* special codegen doesn't succeed, first_lir_insn_ will
* set to NULL;
*/
+ cu_->NewTimingSplit("SpecialMIR2LIR");
SpecialMIR2LIR(mir_graph_->GetSpecialCase());
}
@@ -1091,5 +1055,4 @@ void Mir2Lir::InsertLIRAfter(LIR* current_lir, LIR* new_lir) {
new_lir->next->prev = new_lir;
}
-
} // namespace art
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index f018c61819..df6493dc77 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -30,16 +30,17 @@ namespace art {
*/
/*
- * Generate an kPseudoBarrier marker to indicate the boundary of special
+ * Generate a kPseudoBarrier marker to indicate the boundary of special
* blocks.
*/
void Mir2Lir::GenBarrier() {
LIR* barrier = NewLIR0(kPseudoBarrier);
/* Mark all resources as being clobbered */
- barrier->def_mask = -1;
+ DCHECK(!barrier->flags.use_def_invalid);
+ barrier->u.m.def_mask = ENCODE_ALL;
}
-// FIXME: need to do some work to split out targets with
+// TODO: need to do some work to split out targets with
// condition codes and those without
LIR* Mir2Lir::GenCheck(ConditionCode c_code, ThrowKind kind) {
DCHECK_NE(cu_->instruction_set, kMips);
@@ -65,8 +66,7 @@ LIR* Mir2Lir::GenImmedCheck(ConditionCode c_code, int reg, int imm_val, ThrowKin
/* Perform null-check on a register. */
LIR* Mir2Lir::GenNullCheck(int s_reg, int m_reg, int opt_flags) {
- if (!(cu_->disable_opt & (1 << kNullCheckElimination)) &&
- opt_flags & MIR_IGNORE_NULL_CHECK) {
+ if (!(cu_->disable_opt & (1 << kNullCheckElimination)) && (opt_flags & MIR_IGNORE_NULL_CHECK)) {
return NULL;
}
return GenImmedCheck(kCondEq, m_reg, 0, kThrowNullPointer);
@@ -127,13 +127,11 @@ void Mir2Lir::GenCompareAndBranch(Instruction::Code opcode, RegLocation rl_src1,
InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src2))) {
// OK - convert this to a compare immediate and branch
OpCmpImmBranch(cond, rl_src1.low_reg, mir_graph_->ConstantValue(rl_src2), taken);
- OpUnconditionalBranch(fall_through);
return;
}
}
rl_src2 = LoadValue(rl_src2, kCoreReg);
OpCmpBranch(cond, rl_src1.low_reg, rl_src2.low_reg, taken);
- OpUnconditionalBranch(fall_through);
}
void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_src, LIR* taken,
@@ -164,7 +162,6 @@ void Mir2Lir::GenCompareZeroAndBranch(Instruction::Code opcode, RegLocation rl_s
LOG(FATAL) << "Unexpected opcode " << opcode;
}
OpCmpImmBranch(cond, rl_src.low_reg, 0, taken);
- OpUnconditionalBranch(fall_through);
}
void Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) {
@@ -337,8 +334,8 @@ void Mir2Lir::GenSput(uint32_t field_idx, RegLocation rl_src, bool is_long_or_do
bool is_volatile;
bool is_referrers_class;
bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo(
- field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index,
- is_referrers_class, is_volatile, true);
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), true,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
DCHECK_GE(field_offset, 0);
int rBase;
@@ -423,8 +420,8 @@ void Mir2Lir::GenSget(uint32_t field_idx, RegLocation rl_dest,
bool is_volatile;
bool is_referrers_class;
bool fast_path = cu_->compiler_driver->ComputeStaticFieldInfo(
- field_idx, mir_graph_->GetCurrentDexCompilationUnit(), field_offset, ssb_index,
- is_referrers_class, is_volatile, false);
+ field_idx, mir_graph_->GetCurrentDexCompilationUnit(), false,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
DCHECK_GE(field_offset, 0);
int rBase;
@@ -506,7 +503,7 @@ void Mir2Lir::HandleSuspendLaunchPads() {
ResetRegPool();
ResetDefTracking();
LIR* lab = suspend_launchpads_.Get(i);
- LIR* resume_lab = reinterpret_cast<LIR*>(lab->operands[0]);
+ LIR* resume_lab = reinterpret_cast<LIR*>(UnwrapPointer(lab->operands[0]));
current_dalvik_offset_ = lab->operands[1];
AppendLIR(lab);
int r_tgt = CallHelperSetup(helper_offset);
@@ -521,12 +518,12 @@ void Mir2Lir::HandleIntrinsicLaunchPads() {
ResetRegPool();
ResetDefTracking();
LIR* lab = intrinsic_launchpads_.Get(i);
- CallInfo* info = reinterpret_cast<CallInfo*>(lab->operands[0]);
+ CallInfo* info = reinterpret_cast<CallInfo*>(UnwrapPointer(lab->operands[0]));
current_dalvik_offset_ = info->offset;
AppendLIR(lab);
// NOTE: GenInvoke handles MarkSafepointPC
GenInvoke(info);
- LIR* resume_lab = reinterpret_cast<LIR*>(lab->operands[2]);
+ LIR* resume_lab = reinterpret_cast<LIR*>(UnwrapPointer(lab->operands[2]));
if (resume_lab != NULL) {
OpUnconditionalBranch(resume_lab);
}
@@ -626,7 +623,7 @@ void Mir2Lir::GenIGet(uint32_t field_idx, int opt_flags, OpSize size,
int field_offset;
bool is_volatile;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile, false);
+ bool fast_path = FastInstance(field_idx, false, &field_offset, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
RegLocation rl_result;
@@ -687,8 +684,7 @@ void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
int field_offset;
bool is_volatile;
- bool fast_path = FastInstance(field_idx, field_offset, is_volatile,
- true);
+ bool fast_path = FastInstance(field_idx, true, &field_offset, &is_volatile);
if (fast_path && !SLOW_FIELD_PATH) {
RegisterClass reg_class = oat_reg_class_by_size(size);
DCHECK_GE(field_offset, 0);
@@ -730,6 +726,18 @@ void Mir2Lir::GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
}
}
+void Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src) {
+ bool needs_range_check = !(opt_flags & MIR_IGNORE_RANGE_CHECK);
+ bool needs_null_check = !((cu_->disable_opt & (1 << kNullCheckElimination)) &&
+ (opt_flags & MIR_IGNORE_NULL_CHECK));
+ ThreadOffset helper = needs_range_check
+ ? (needs_null_check ? QUICK_ENTRYPOINT_OFFSET(pAputObjectWithNullAndBoundCheck)
+ : QUICK_ENTRYPOINT_OFFSET(pAputObjectWithBoundCheck))
+ : QUICK_ENTRYPOINT_OFFSET(pAputObject);
+ CallRuntimeHelperRegLocationRegLocationRegLocation(helper, rl_array, rl_index, rl_src, true);
+}
+
void Mir2Lir::GenConstClass(uint32_t type_idx, RegLocation rl_dest) {
RegLocation rl_method = LoadCurrMethod();
int res_reg = AllocTemp();
@@ -1113,8 +1121,8 @@ void Mir2Lir::GenCheckCast(uint32_t insn_idx, uint32_t type_idx, RegLocation rl_
if (!type_known_abstract) {
branch2 = OpCmpBranch(kCondEq, TargetReg(kArg1), class_reg, NULL);
}
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCheckCast), TargetReg(kArg1),
- TargetReg(kArg2), true);
+ CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCheckCast), TargetReg(kArg2),
+ TargetReg(kArg1), true);
/* branch target here */
LIR* target = NewLIR0(kPseudoTargetLabel);
branch1->target = target;
@@ -1299,6 +1307,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
}
StoreValue(rl_dest, rl_result);
} else {
+ bool done = false; // Set to true if we happen to find a way to use a real instruction.
if (cu_->instruction_set == kMips) {
rl_src1 = LoadValue(rl_src1, kCoreReg);
rl_src2 = LoadValue(rl_src2, kCoreReg);
@@ -1306,7 +1315,23 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero);
}
rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv);
- } else {
+ done = true;
+ } else if (cu_->instruction_set == kThumb2) {
+ if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ // Use ARM SDIV instruction for division. For remainder we also need to
+ // calculate using a MUL and subtract.
+ rl_src1 = LoadValue(rl_src1, kCoreReg);
+ rl_src2 = LoadValue(rl_src2, kCoreReg);
+ if (check_zero) {
+ GenImmedCheck(kCondEq, rl_src2.low_reg, 0, kThrowDivZero);
+ }
+ rl_result = GenDivRem(rl_dest, rl_src1.low_reg, rl_src2.low_reg, op == kOpDiv);
+ done = true;
+ }
+ }
+
+ // If we haven't already generated the code use the callout function.
+ if (!done) {
ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pIdivmod);
FlushAllRegs(); /* Send everything to home location */
LoadValueDirectFixed(rl_src2, TargetReg(kArg1));
@@ -1315,7 +1340,7 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
if (check_zero) {
GenImmedCheck(kCondEq, TargetReg(kArg1), 0, kThrowDivZero);
}
- // NOTE: callout here is not a safepoint
+ // NOTE: callout here is not a safepoint.
CallHelper(r_tgt, func_offset, false /* not a safepoint */);
if (op == kOpDiv)
rl_result = GetReturn(false);
@@ -1343,7 +1368,7 @@ static bool IsPopCountLE2(unsigned int x) {
}
// Returns the index of the lowest set bit in 'x'.
-static int LowestSetBit(unsigned int x) {
+static int32_t LowestSetBit(uint32_t x) {
int bit_posn = 0;
while ((x & 0xf) == 0) {
bit_posn += 4;
@@ -1553,11 +1578,24 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re
if (HandleEasyDivRem(opcode, is_div, rl_src, rl_dest, lit)) {
return;
}
+
+ bool done = false;
if (cu_->instruction_set == kMips) {
rl_src = LoadValue(rl_src, kCoreReg);
rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div);
- } else {
- FlushAllRegs(); /* Everything to home location */
+ done = true;
+ } else if (cu_->instruction_set == kThumb2) {
+ if (cu_->GetInstructionSetFeatures().HasDivideInstruction()) {
+ // Use ARM SDIV instruction for division. For remainder we also need to
+ // calculate using a MUL and subtract.
+ rl_src = LoadValue(rl_src, kCoreReg);
+ rl_result = GenDivRemLit(rl_dest, rl_src.low_reg, lit, is_div);
+ done = true;
+ }
+ }
+
+ if (!done) {
+ FlushAllRegs(); /* Everything to home location. */
LoadValueDirectFixed(rl_src, TargetReg(kArg0));
Clobber(TargetReg(kArg0));
ThreadOffset func_offset = QUICK_ENTRYPOINT_OFFSET(pIdivmod);
@@ -1575,7 +1613,7 @@ void Mir2Lir::GenArithOpIntLit(Instruction::Code opcode, RegLocation rl_dest, Re
}
rl_src = LoadValue(rl_src, kCoreReg);
rl_result = EvalLoc(rl_dest, kCoreReg, true);
- // Avoid shifts by literal 0 - no support in Thumb. Change to copy
+ // Avoid shifts by literal 0 - no support in Thumb. Change to copy.
if (shift_op && (lit == 0)) {
OpRegCopy(rl_result.low_reg, rl_src.low_reg);
} else {
@@ -1651,7 +1689,7 @@ void Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest,
case Instruction::REM_LONG_2ADDR:
call_out = true;
check_zero = true;
- func_offset = QUICK_ENTRYPOINT_OFFSET(pLdivmod);
+ func_offset = QUICK_ENTRYPOINT_OFFSET(pLmod);
/* NOTE - for Arm, result is in kArg2/kArg3 instead of kRet0/kRet1 */
ret_reg = (cu_->instruction_set == kThumb2) ? TargetReg(kArg2) : TargetReg(kRet0);
break;
@@ -1744,8 +1782,8 @@ void Mir2Lir::GenSuspendTest(int opt_flags) {
FlushAllRegs();
LIR* branch = OpTestSuspend(NULL);
LIR* ret_lab = NewLIR0(kPseudoTargetLabel);
- LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget,
- reinterpret_cast<uintptr_t>(ret_lab), current_dalvik_offset_);
+ LIR* target = RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, WrapPointer(ret_lab),
+ current_dalvik_offset_);
branch->target = target;
suspend_launchpads_.Insert(target);
}
@@ -1758,11 +1796,23 @@ void Mir2Lir::GenSuspendTestAndBranch(int opt_flags, LIR* target) {
}
OpTestSuspend(target);
LIR* launch_pad =
- RawLIR(current_dalvik_offset_, kPseudoSuspendTarget,
- reinterpret_cast<uintptr_t>(target), current_dalvik_offset_);
+ RawLIR(current_dalvik_offset_, kPseudoSuspendTarget, WrapPointer(target),
+ current_dalvik_offset_);
FlushAllRegs();
OpUnconditionalBranch(launch_pad);
suspend_launchpads_.Insert(launch_pad);
}
+/* Call out to helper assembly routine that will null check obj and then lock it. */
+void Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
+ FlushAllRegs();
+ CallRuntimeHelperRegLocation(QUICK_ENTRYPOINT_OFFSET(pLockObject), rl_src, true);
+}
+
+/* Call out to helper assembly routine that will null check obj and then unlock it. */
+void Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
+ FlushAllRegs();
+ CallRuntimeHelperRegLocation(QUICK_ENTRYPOINT_OFFSET(pUnlockObject), rl_src, true);
+}
+
} // namespace art
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 2a0a23c7cd..7225262647 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -214,6 +214,7 @@ void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_off
int arg0, RegLocation arg1,
RegLocation arg2, bool safepoint_pc) {
int r_tgt = CallHelperSetup(helper_offset);
+ DCHECK_EQ(arg1.wide, 0U);
LoadValueDirectFixed(arg1, TargetReg(kArg1));
if (arg2.wide == 0) {
LoadValueDirectFixed(arg2, TargetReg(kArg2));
@@ -225,6 +226,21 @@ void Mir2Lir::CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_off
CallHelper(r_tgt, helper_offset, safepoint_pc);
}
+void Mir2Lir::CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset,
+ RegLocation arg0, RegLocation arg1,
+ RegLocation arg2,
+ bool safepoint_pc) {
+ int r_tgt = CallHelperSetup(helper_offset);
+ DCHECK_EQ(arg0.wide, 0U);
+ LoadValueDirectFixed(arg0, TargetReg(kArg0));
+ DCHECK_EQ(arg1.wide, 0U);
+ LoadValueDirectFixed(arg1, TargetReg(kArg1));
+ DCHECK_EQ(arg1.wide, 0U);
+ LoadValueDirectFixed(arg2, TargetReg(kArg2));
+ ClobberCalleeSave();
+ CallHelper(r_tgt, helper_offset, safepoint_pc);
+}
+
/*
* If there are any ins passed in registers that have not been promoted
* to a callee-save register, flush them to the frame. Perform intial
@@ -334,16 +350,13 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
uintptr_t direct_code, uintptr_t direct_method,
InvokeType type) {
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- if (cu->instruction_set != kThumb2) {
- // Disable sharpening
- direct_code = 0;
- direct_method = 0;
- }
if (direct_code != 0 && direct_method != 0) {
switch (state) {
case 0: // Get the current Method* [sets kArg0]
if (direct_code != static_cast<unsigned int>(-1)) {
- cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
+ if (cu->instruction_set != kX86) {
+ cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
+ }
} else {
CHECK_EQ(cu->dex_file, target_method.dex_file);
LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_,
@@ -389,6 +402,7 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info,
cg->LoadConstant(cg->TargetReg(kInvokeTgt), direct_code);
} else {
CHECK_EQ(cu->dex_file, target_method.dex_file);
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
LIR* data_target = cg->ScanLiteralPool(cg->code_literal_list_,
target_method.dex_method_index, 0);
if (data_target == NULL) {
@@ -477,73 +491,56 @@ static int NextVCallInsn(CompilationUnit* cu, CallInfo* info,
}
/*
- * All invoke-interface calls bounce off of art_quick_invoke_interface_trampoline,
- * which will locate the target and continue on via a tail call.
+ * Emit the next instruction in an invoke interface sequence. This will do a lookup in the
+ * class's IMT, calling either the actual method or art_quick_imt_conflict_trampoline if
+ * more than one interface method map to the same index. Note also that we'll load the first
+ * argument ("this") into kArg1 here rather than the standard LoadArgRegs.
*/
static int NextInterfaceCallInsn(CompilationUnit* cu, CallInfo* info, int state,
const MethodReference& target_method,
- uint32_t unused, uintptr_t unused2,
- uintptr_t direct_method, InvokeType unused4) {
+ uint32_t method_idx, uintptr_t unused,
+ uintptr_t direct_method, InvokeType unused2) {
Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
- if (cu->instruction_set != kThumb2) {
- // Disable sharpening
- direct_method = 0;
- }
- ThreadOffset trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline);
- if (direct_method != 0) {
- switch (state) {
- case 0: // Load the trampoline target [sets kInvokeTgt].
- if (cu->instruction_set != kX86) {
- cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(),
- cg->TargetReg(kInvokeTgt));
- }
- // Get the interface Method* [sets kArg0]
- if (direct_method != static_cast<unsigned int>(-1)) {
- cg->LoadConstant(cg->TargetReg(kArg0), direct_method);
- } else {
- CHECK_EQ(cu->dex_file, target_method.dex_file);
- LIR* data_target = cg->ScanLiteralPool(cg->method_literal_list_,
- target_method.dex_method_index, 0);
- if (data_target == NULL) {
- data_target = cg->AddWordData(&cg->method_literal_list_,
- target_method.dex_method_index);
- data_target->operands[1] = kInterface;
- }
- LIR* load_pc_rel = cg->OpPcRelLoad(cg->TargetReg(kArg0), data_target);
- cg->AppendLIR(load_pc_rel);
- DCHECK_EQ(cu->instruction_set, kThumb2) << reinterpret_cast<void*>(data_target);
- }
- break;
- default:
- return -1;
+ switch (state) {
+ case 0: // Set target method index in case of conflict [set kHiddenArg, kHiddenFpArg (x86)]
+ CHECK_EQ(cu->dex_file, target_method.dex_file);
+ CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
+ cg->LoadConstant(cg->TargetReg(kHiddenArg), target_method.dex_method_index);
+ if (cu->instruction_set == kX86) {
+ cg->OpRegCopy(cg->TargetReg(kHiddenFpArg), cg->TargetReg(kHiddenArg));
+ }
+ break;
+ case 1: { // Get "this" [set kArg1]
+ RegLocation rl_arg = info->args[0];
+ cg->LoadValueDirectFixed(rl_arg, cg->TargetReg(kArg1));
+ break;
}
- } else {
- switch (state) {
- case 0:
- // Get the current Method* [sets kArg0] - TUNING: remove copy of method if it is promoted.
- cg->LoadCurrMethodDirect(cg->TargetReg(kArg0));
- // Load the trampoline target [sets kInvokeTgt].
- if (cu->instruction_set != kX86) {
- cg->LoadWordDisp(cg->TargetReg(kSelf), trampoline.Int32Value(),
- cg->TargetReg(kInvokeTgt));
- }
- break;
- case 1: // Get method->dex_cache_resolved_methods_ [set/use kArg0]
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
- cg->TargetReg(kArg0));
+ case 2: // Is "this" null? [use kArg1]
+ cg->GenNullCheck(info->args[0].s_reg_low, cg->TargetReg(kArg1), info->opt_flags);
+ // Get this->klass_ [use kArg1, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kArg1), mirror::Object::ClassOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
break;
- case 2: // Grab target method* [set/use kArg0]
- CHECK_EQ(cu->dex_file, target_method.dex_file);
- cg->LoadWordDisp(cg->TargetReg(kArg0),
- mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value() +
- (target_method.dex_method_index * 4),
+ case 3: // Get this->klass_->imtable [use kInvokeTgt, set kInvokeTgt]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), mirror::Class::ImTableOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ case 4: // Get target method [use kInvokeTgt, set kArg0]
+ cg->LoadWordDisp(cg->TargetReg(kInvokeTgt), ((method_idx % ClassLinker::kImtSize) * 4) +
+ mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value(),
cg->TargetReg(kArg0));
break;
+ case 5: // Get the compiled code address [use kArg0, set kInvokeTgt]
+ if (cu->instruction_set != kX86) {
+ cg->LoadWordDisp(cg->TargetReg(kArg0),
+ mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value(),
+ cg->TargetReg(kInvokeTgt));
+ break;
+ }
+ // Intentional fallthrough for X86
default:
return -1;
- }
}
return state + 1;
}
@@ -810,7 +807,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
OpRegRegImm(kOpAdd, TargetReg(kArg3), TargetReg(kSp), start_offset);
LIR* ld = OpVldm(TargetReg(kArg3), regs_left);
// TUNING: loosen barrier
- ld->def_mask = ENCODE_ALL;
+ ld->u.m.def_mask = ENCODE_ALL;
SetMemRefType(ld, true /* is_load */, kDalvikReg);
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
direct_code, direct_method, type);
@@ -819,7 +816,7 @@ int Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
direct_code, direct_method, type);
LIR* st = OpVstm(TargetReg(kArg3), regs_left);
SetMemRefType(st, false /* is_load */, kDalvikReg);
- st->def_mask = ENCODE_ALL;
+ st->u.m.def_mask = ENCODE_ALL;
call_state = next_call_insn(cu_, info, call_state, target_method, vtable_idx,
direct_code, direct_method, type);
}
@@ -892,7 +889,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
LoadWordDisp(rl_obj.low_reg, value_offset, reg_ptr);
if (range_check) {
// Set up a launch pad to allow retry in case of bounds violation */
- launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpRegReg(kOpCmp, rl_idx.low_reg, reg_max);
FreeTemp(reg_max);
@@ -903,7 +900,7 @@ bool Mir2Lir::GenInlinedCharAt(CallInfo* info) {
reg_max = AllocTemp();
LoadWordDisp(rl_obj.low_reg, count_offset, reg_max);
// Set up a launch pad to allow retry in case of bounds violation */
- launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpRegReg(kOpCmp, rl_idx.low_reg, reg_max);
FreeTemp(reg_max);
@@ -961,6 +958,31 @@ bool Mir2Lir::GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty) {
return true;
}
+bool Mir2Lir::GenInlinedReverseBytes(CallInfo* info, OpSize size) {
+ if (cu_->instruction_set == kMips) {
+ // TODO - add Mips implementation
+ return false;
+ }
+ RegLocation rl_src_i = info->args[0];
+ RegLocation rl_dest = InlineTarget(info); // result reg
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == kLong) {
+ RegLocation rl_i = LoadValueWide(rl_src_i, kCoreReg);
+ int reg_tmp = AllocTemp();
+ OpRegCopy(reg_tmp, rl_result.low_reg);
+ OpRegReg(kOpRev, rl_result.low_reg, rl_i.high_reg);
+ OpRegReg(kOpRev, rl_result.high_reg, reg_tmp);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ DCHECK(size == kWord || size == kSignedHalf);
+ OpKind op = (size == kWord) ? kOpRev : kOpRevsh;
+ RegLocation rl_i = LoadValue(rl_src_i, kCoreReg);
+ OpRegReg(op, rl_result.low_reg, rl_i.low_reg);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
bool Mir2Lir::GenInlinedAbsInt(CallInfo* info) {
if (cu_->instruction_set == kMips) {
// TODO - add Mips implementation
@@ -1069,7 +1091,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
}
int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0;
GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags);
- LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad);
// NOTE: not a safepoint
@@ -1079,7 +1101,7 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf));
}
LIR* resume_tgt = NewLIR0(kPseudoTargetLabel);
- launch_pad->operands[2] = reinterpret_cast<uintptr_t>(resume_tgt);
+ launch_pad->operands[2] = WrapPointer(resume_tgt);
// Record that we've already inlined & null checked
info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK);
RegLocation rl_return = GetReturn(false);
@@ -1107,7 +1129,7 @@ bool Mir2Lir::GenInlinedStringCompareTo(CallInfo* info) {
LoadHelper(QUICK_ENTRYPOINT_OFFSET(pStringCompareTo)) : 0;
GenNullCheck(rl_this.s_reg_low, reg_this, info->opt_flags);
// TUNING: check if rl_cmp.s_reg_low is already null checked
- LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, reinterpret_cast<uintptr_t>(info));
+ LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info));
intrinsic_launchpads_.Insert(launch_pad);
OpCmpImmBranch(kCondEq, reg_cmp, 0, launch_pad);
// NOTE: not a safepoint
@@ -1219,8 +1241,10 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) {
* method. By doing this during basic block construction, we can also
* take advantage of/generate new useful dataflow info.
*/
+ const DexFile::MethodId& target_mid = cu_->dex_file->GetMethodId(info->index);
+ const DexFile::TypeId& declaring_type = cu_->dex_file->GetTypeId(target_mid.class_idx_);
StringPiece tgt_methods_declaring_class(
- cu_->dex_file->GetMethodDeclaringClassDescriptor(cu_->dex_file->GetMethodId(info->index)));
+ cu_->dex_file->StringDataByIdx(declaring_type.descriptor_idx_));
if (tgt_methods_declaring_class.starts_with("Ljava/lang/Double;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
if (tgt_method == "long java.lang.Double.doubleToRawLongBits(double)") {
@@ -1231,12 +1255,22 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) {
}
} else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Float;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
- if (tgt_method == "int java.lang.Float.float_to_raw_int_bits(float)") {
+ if (tgt_method == "int java.lang.Float.floatToRawIntBits(float)") {
return GenInlinedFloatCvt(info);
}
if (tgt_method == "float java.lang.Float.intBitsToFloat(int)") {
return GenInlinedFloatCvt(info);
}
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Integer;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "int java.lang.Integer.reverseBytes(int)") {
+ return GenInlinedReverseBytes(info, kWord);
+ }
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Long;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "long java.lang.Long.reverseBytes(long)") {
+ return GenInlinedReverseBytes(info, kLong);
+ }
} else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Math;") ||
tgt_methods_declaring_class.starts_with("Ljava/lang/StrictMath;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
@@ -1260,6 +1294,11 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) {
tgt_method == "double java.lang.StrictMath.sqrt(double)") {
return GenInlinedSqrt(info);
}
+ } else if (tgt_methods_declaring_class.starts_with("Ljava/lang/Short;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "short java.lang.Short.reverseBytes(short)") {
+ return GenInlinedReverseBytes(info, kSignedHalf);
+ }
} else if (tgt_methods_declaring_class.starts_with("Ljava/lang/String;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
if (tgt_method == "char java.lang.String.charAt(int)") {
@@ -1285,6 +1324,32 @@ bool Mir2Lir::GenIntrinsic(CallInfo* info) {
if (tgt_method == "java.lang.Thread java.lang.Thread.currentThread()") {
return GenInlinedCurrentThread(info);
}
+ } else if (tgt_methods_declaring_class.starts_with("Llibcore/io/Memory;")) {
+ std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
+ if (tgt_method == "byte libcore.io.Memory.peekByte(long)") {
+ return GenInlinedPeek(info, kSignedByte);
+ }
+ if (tgt_method == "int libcore.io.Memory.peekIntNative(long)") {
+ return GenInlinedPeek(info, kWord);
+ }
+ if (tgt_method == "long libcore.io.Memory.peekLongNative(long)") {
+ return GenInlinedPeek(info, kLong);
+ }
+ if (tgt_method == "short libcore.io.Memory.peekShortNative(long)") {
+ return GenInlinedPeek(info, kSignedHalf);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeByte(long, byte)") {
+ return GenInlinedPoke(info, kSignedByte);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeIntNative(long, int)") {
+ return GenInlinedPoke(info, kWord);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeLongNative(long, long)") {
+ return GenInlinedPoke(info, kLong);
+ }
+ if (tgt_method == "void libcore.io.Memory.pokeShortNative(long, short)") {
+ return GenInlinedPoke(info, kSignedHalf);
+ }
} else if (tgt_methods_declaring_class.starts_with("Lsun/misc/Unsafe;")) {
std::string tgt_method(PrettyMethod(info->index, *cu_->dex_file));
if (tgt_method == "boolean sun.misc.Unsafe.compareAndSwapInt(java.lang.Object, long, int, int)") {
@@ -1373,16 +1438,13 @@ void Mir2Lir::GenInvoke(CallInfo* info) {
bool fast_path =
cu_->compiler_driver->ComputeInvokeInfo(mir_graph_->GetCurrentDexCompilationUnit(),
current_dalvik_offset_,
- info->type, target_method,
- vtable_idx,
- direct_code, direct_method,
- true) && !SLOW_INVOKE_PATH;
+ true, true,
+ &info->type, &target_method,
+ &vtable_idx,
+ &direct_code, &direct_method) && !SLOW_INVOKE_PATH;
if (info->type == kInterface) {
- if (fast_path) {
- p_null_ck = &null_ck;
- }
next_call_insn = fast_path ? NextInterfaceCallInsn : NextInterfaceCallInsnWithAccessCheck;
- skip_this = false;
+ skip_this = fast_path;
} else if (info->type == kDirect) {
if (fast_path) {
p_null_ck = &null_ck;
@@ -1422,15 +1484,14 @@ void Mir2Lir::GenInvoke(CallInfo* info) {
if (cu_->instruction_set != kX86) {
call_inst = OpReg(kOpBlx, TargetReg(kInvokeTgt));
} else {
- if (fast_path && info->type != kInterface) {
+ if (fast_path) {
call_inst = OpMem(kOpBlx, TargetReg(kArg0),
mirror::ArtMethod::GetEntryPointFromCompiledCodeOffset().Int32Value());
} else {
ThreadOffset trampoline(-1);
switch (info->type) {
case kInterface:
- trampoline = fast_path ? QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampoline)
- : QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
+ trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeInterfaceTrampolineWithAccessCheck);
break;
case kDirect:
trampoline = QUICK_ENTRYPOINT_OFFSET(pInvokeDirectTrampolineWithAccessCheck);
diff --git a/compiler/dex/quick/local_optimizations.cc b/compiler/dex/quick/local_optimizations.cc
index 630e990733..0f29578c4e 100644
--- a/compiler/dex/quick/local_optimizations.cc
+++ b/compiler/dex/quick/local_optimizations.cc
@@ -21,8 +21,8 @@ namespace art {
#define DEBUG_OPT(X)
/* Check RAW, WAR, and RAW dependency on the register operands */
-#define CHECK_REG_DEP(use, def, check) ((def & check->use_mask) || \
- ((use | def) & check->def_mask))
+#define CHECK_REG_DEP(use, def, check) ((def & check->u.m.use_mask) || \
+ ((use | def) & check->u.m.def_mask))
/* Scheduler heuristics */
#define MAX_HOIST_DISTANCE 20
@@ -30,10 +30,10 @@ namespace art {
#define LD_LATENCY 2
static bool IsDalvikRegisterClobbered(LIR* lir1, LIR* lir2) {
- int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->alias_info);
- int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->alias_info);
- int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->alias_info);
- int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->alias_info);
+ int reg1Lo = DECODE_ALIAS_INFO_REG(lir1->flags.alias_info);
+ int reg1Hi = reg1Lo + DECODE_ALIAS_INFO_WIDE(lir1->flags.alias_info);
+ int reg2Lo = DECODE_ALIAS_INFO_REG(lir2->flags.alias_info);
+ int reg2Hi = reg2Lo + DECODE_ALIAS_INFO_WIDE(lir2->flags.alias_info);
return (reg1Lo == reg2Lo) || (reg1Lo == reg2Hi) || (reg1Hi == reg2Lo);
}
@@ -78,7 +78,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
}
for (this_lir = PREV_LIR(tail_lir); this_lir != head_lir; this_lir = PREV_LIR(this_lir)) {
- if (is_pseudo_opcode(this_lir->opcode)) {
+ if (IsPseudoLirOp(this_lir->opcode)) {
continue;
}
@@ -99,15 +99,14 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
int native_reg_id;
if (cu_->instruction_set == kX86) {
// If x86, location differs depending on whether memory/reg operation.
- native_reg_id = (GetTargetInstFlags(this_lir->opcode) & IS_STORE) ? this_lir->operands[2]
- : this_lir->operands[0];
+ native_reg_id = (target_flags & IS_STORE) ? this_lir->operands[2] : this_lir->operands[0];
} else {
native_reg_id = this_lir->operands[0];
}
- bool is_this_lir_load = GetTargetInstFlags(this_lir->opcode) & IS_LOAD;
+ bool is_this_lir_load = target_flags & IS_LOAD;
LIR* check_lir;
/* Use the mem mask to determine the rough memory location */
- uint64_t this_mem_mask = (this_lir->use_mask | this_lir->def_mask) & ENCODE_MEM;
+ uint64_t this_mem_mask = (this_lir->u.m.use_mask | this_lir->u.m.def_mask) & ENCODE_MEM;
/*
* Currently only eliminate redundant ld/st for constant and Dalvik
@@ -117,10 +116,10 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
continue;
}
- uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM;
+ uint64_t stop_def_reg_mask = this_lir->u.m.def_mask & ~ENCODE_MEM;
uint64_t stop_use_reg_mask;
if (cu_->instruction_set == kX86) {
- stop_use_reg_mask = (IS_BRANCH | this_lir->use_mask) & ~ENCODE_MEM;
+ stop_use_reg_mask = (IS_BRANCH | this_lir->u.m.use_mask) & ~ENCODE_MEM;
} else {
/*
* Add pc to the resource mask to prevent this instruction
@@ -128,7 +127,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
* region bits since stop_mask is used to check data/control
* dependencies.
*/
- stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->use_mask) & ~ENCODE_MEM;
+ stop_use_reg_mask = (GetPCUseDefEncoding() | this_lir->u.m.use_mask) & ~ENCODE_MEM;
}
for (check_lir = NEXT_LIR(this_lir); check_lir != tail_lir; check_lir = NEXT_LIR(check_lir)) {
@@ -136,11 +135,11 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
* Skip already dead instructions (whose dataflow information is
* outdated and misleading).
*/
- if (check_lir->flags.is_nop || is_pseudo_opcode(check_lir->opcode)) {
+ if (check_lir->flags.is_nop || IsPseudoLirOp(check_lir->opcode)) {
continue;
}
- uint64_t check_mem_mask = (check_lir->use_mask | check_lir->def_mask) & ENCODE_MEM;
+ uint64_t check_mem_mask = (check_lir->u.m.use_mask | check_lir->u.m.def_mask) & ENCODE_MEM;
uint64_t alias_condition = this_mem_mask & check_mem_mask;
bool stop_here = false;
@@ -160,7 +159,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
*/
DCHECK(!(check_flags & IS_STORE));
/* Same value && same register type */
- if (check_lir->alias_info == this_lir->alias_info &&
+ if (check_lir->flags.alias_info == this_lir->flags.alias_info &&
SameRegType(check_lir->operands[0], native_reg_id)) {
/*
* Different destination register - insert
@@ -169,11 +168,11 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
if (check_lir->operands[0] != native_reg_id) {
ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id);
}
- check_lir->flags.is_nop = true;
+ NopLIR(check_lir);
}
} else if (alias_condition == ENCODE_DALVIK_REG) {
/* Must alias */
- if (check_lir->alias_info == this_lir->alias_info) {
+ if (check_lir->flags.alias_info == this_lir->flags.alias_info) {
/* Only optimize compatible registers */
bool reg_compatible = SameRegType(check_lir->operands[0], native_reg_id);
if ((is_this_lir_load && is_check_lir_load) ||
@@ -188,7 +187,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
native_reg_id) {
ConvertMemOpIntoMove(check_lir, check_lir->operands[0], native_reg_id);
}
- check_lir->flags.is_nop = true;
+ NopLIR(check_lir);
} else {
/*
* Destinaions are of different types -
@@ -202,7 +201,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
stop_here = true;
} else if (!is_this_lir_load && !is_check_lir_load) {
/* WAW - nuke the earlier store */
- this_lir->flags.is_nop = true;
+ NopLIR(this_lir);
stop_here = true;
}
/* Partial overlap */
@@ -257,7 +256,7 @@ void Mir2Lir::ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir) {
* top-down order.
*/
InsertLIRBefore(check_lir, new_store_lir);
- this_lir->flags.is_nop = true;
+ NopLIR(this_lir);
}
break;
} else if (!check_lir->flags.is_nop) {
@@ -286,7 +285,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
/* Start from the second instruction */
for (this_lir = NEXT_LIR(head_lir); this_lir != tail_lir; this_lir = NEXT_LIR(this_lir)) {
- if (is_pseudo_opcode(this_lir->opcode)) {
+ if (IsPseudoLirOp(this_lir->opcode)) {
continue;
}
@@ -298,7 +297,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
continue;
}
- uint64_t stop_use_all_mask = this_lir->use_mask;
+ uint64_t stop_use_all_mask = this_lir->u.m.use_mask;
if (cu_->instruction_set != kX86) {
/*
@@ -314,7 +313,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
/* Similar as above, but just check for pure register dependency */
uint64_t stop_use_reg_mask = stop_use_all_mask & ~ENCODE_MEM;
- uint64_t stop_def_reg_mask = this_lir->def_mask & ~ENCODE_MEM;
+ uint64_t stop_def_reg_mask = this_lir->u.m.def_mask & ~ENCODE_MEM;
int next_slot = 0;
bool stop_here = false;
@@ -329,7 +328,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
continue;
}
- uint64_t check_mem_mask = check_lir->def_mask & ENCODE_MEM;
+ uint64_t check_mem_mask = check_lir->u.m.def_mask & ENCODE_MEM;
uint64_t alias_condition = stop_use_all_mask & check_mem_mask;
stop_here = false;
@@ -338,7 +337,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
/* We can fully disambiguate Dalvik references */
if (alias_condition == ENCODE_DALVIK_REG) {
/* Must alias or partually overlap */
- if ((check_lir->alias_info == this_lir->alias_info) ||
+ if ((check_lir->flags.alias_info == this_lir->flags.alias_info) ||
IsDalvikRegisterClobbered(this_lir, check_lir)) {
stop_here = true;
}
@@ -363,7 +362,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
* Store the dependent or non-pseudo/indepedent instruction to the
* list.
*/
- if (stop_here || !is_pseudo_opcode(check_lir->opcode)) {
+ if (stop_here || !IsPseudoLirOp(check_lir->opcode)) {
prev_inst_list[next_slot++] = check_lir;
if (next_slot == MAX_HOIST_DISTANCE) {
break;
@@ -394,7 +393,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
int slot;
LIR* dep_lir = prev_inst_list[next_slot-1];
/* If there is ld-ld dependency, wait LDLD_DISTANCE cycles */
- if (!is_pseudo_opcode(dep_lir->opcode) &&
+ if (!IsPseudoLirOp(dep_lir->opcode) &&
(GetTargetInstFlags(dep_lir->opcode) & IS_LOAD)) {
first_slot -= LDLD_DISTANCE;
}
@@ -407,7 +406,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
LIR* prev_lir = prev_inst_list[slot+1];
/* Check the highest instruction */
- if (prev_lir->def_mask == ENCODE_ALL) {
+ if (prev_lir->u.m.def_mask == ENCODE_ALL) {
/*
* If the first instruction is a load, don't hoist anything
* above it since it is unlikely to be beneficial.
@@ -435,9 +434,9 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
* Try to find two instructions with load/use dependency until
* the remaining instructions are less than LD_LATENCY.
*/
- bool prev_is_load = is_pseudo_opcode(prev_lir->opcode) ? false :
+ bool prev_is_load = IsPseudoLirOp(prev_lir->opcode) ? false :
(GetTargetInstFlags(prev_lir->opcode) & IS_LOAD);
- if (((cur_lir->use_mask & prev_lir->def_mask) && prev_is_load) || (slot < LD_LATENCY)) {
+ if (((cur_lir->u.m.use_mask & prev_lir->u.m.def_mask) && prev_is_load) || (slot < LD_LATENCY)) {
break;
}
}
@@ -453,7 +452,7 @@ void Mir2Lir::ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir) {
* is never the first LIR on the list
*/
InsertLIRBefore(cur_lir, new_load_lir);
- this_lir->flags.is_nop = true;
+ NopLIR(this_lir);
}
}
}
@@ -468,41 +467,4 @@ void Mir2Lir::ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir) {
}
}
-/*
- * Nop any unconditional branches that go to the next instruction.
- * Note: new redundant branches may be inserted later, and we'll
- * use a check in final instruction assembly to nop those out.
- */
-void Mir2Lir::RemoveRedundantBranches() {
- LIR* this_lir;
-
- for (this_lir = first_lir_insn_; this_lir != last_lir_insn_; this_lir = NEXT_LIR(this_lir)) {
- /* Branch to the next instruction */
- if (IsUnconditionalBranch(this_lir)) {
- LIR* next_lir = this_lir;
-
- while (true) {
- next_lir = NEXT_LIR(next_lir);
-
- /*
- * Is the branch target the next instruction?
- */
- if (next_lir == this_lir->target) {
- this_lir->flags.is_nop = true;
- break;
- }
-
- /*
- * Found real useful stuff between the branch and the target.
- * Need to explicitly check the last_lir_insn_ here because it
- * might be the last real instruction.
- */
- if (!is_pseudo_opcode(next_lir->opcode) ||
- (next_lir == last_lir_insn_))
- break;
- }
- }
- }
-}
-
} // namespace art
diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc
index cd25232c21..5f5e5e44ac 100644
--- a/compiler/dex/quick/mips/assemble_mips.cc
+++ b/compiler/dex/quick/mips/assemble_mips.cc
@@ -489,12 +489,12 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) {
LIR* curr_pc = RawLIR(dalvik_offset, kMipsCurrPC);
InsertLIRBefore(lir, curr_pc);
LIR* anchor = RawLIR(dalvik_offset, kPseudoTargetLabel);
- LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0,
- reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target);
+ LIR* delta_hi = RawLIR(dalvik_offset, kMipsDeltaHi, r_AT, 0, WrapPointer(anchor), 0, 0,
+ lir->target);
InsertLIRBefore(lir, delta_hi);
InsertLIRBefore(lir, anchor);
- LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0,
- reinterpret_cast<uintptr_t>(anchor), 0, 0, lir->target);
+ LIR* delta_lo = RawLIR(dalvik_offset, kMipsDeltaLo, r_AT, 0, WrapPointer(anchor), 0, 0,
+ lir->target);
InsertLIRBefore(lir, delta_lo);
LIR* addu = RawLIR(dalvik_offset, kMipsAddu, r_AT, r_AT, r_RA);
InsertLIRBefore(lir, addu);
@@ -503,7 +503,7 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) {
if (!unconditional) {
InsertLIRBefore(lir, hop_target);
}
- lir->flags.is_nop = true;
+ NopLIR(lir);
}
/*
@@ -512,7 +512,7 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) {
* instruction. In those cases we will try to substitute a new code
* sequence or request that the trace be shortened and retried.
*/
-AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
+AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) {
LIR *lir;
AssemblerStatus res = kSuccess; // Assume success
@@ -526,7 +526,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
continue;
}
- if (lir->flags.pcRelFixup) {
+ if (lir->flags.fixup != kFixupNone) {
if (lir->opcode == kMipsDelta) {
/*
* The "Delta" pseudo-ops load the difference between
@@ -538,8 +538,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
* and is found in lir->target. If operands[3] is non-NULL,
* then it is a Switch/Data table.
*/
- int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset1 = (reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2])))->offset;
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
int delta = offset2 - offset1;
if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) {
@@ -561,25 +561,25 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
RawLIR(lir->dalvik_offset, kMipsAddu,
lir->operands[0], lir->operands[0], r_RA);
InsertLIRBefore(lir, new_addu);
- lir->flags.is_nop = true;
+ NopLIR(lir);
res = kRetryAll;
}
} else if (lir->opcode == kMipsDeltaLo) {
- int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset1 = (reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2])))->offset;
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
int delta = offset2 - offset1;
lir->operands[1] = delta & 0xffff;
} else if (lir->opcode == kMipsDeltaHi) {
- int offset1 = (reinterpret_cast<LIR*>(lir->operands[2]))->offset;
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(lir->operands[3]);
+ int offset1 = (reinterpret_cast<LIR*>(UnwrapPointer(lir->operands[2])))->offset;
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(lir->operands[3]));
int offset2 = tab_rec ? tab_rec->offset : lir->target->offset;
int delta = offset2 - offset1;
lir->operands[1] = (delta >> 16) & 0xffff;
} else if (lir->opcode == kMipsB || lir->opcode == kMipsBal) {
LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (delta & 0x3) {
LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
@@ -592,8 +592,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
}
} else if (lir->opcode >= kMipsBeqz && lir->opcode <= kMipsBnez) {
LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (delta & 0x3) {
LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
@@ -606,8 +606,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
}
} else if (lir->opcode == kMipsBeq || lir->opcode == kMipsBne) {
LIR *target_lir = lir->target;
- uintptr_t pc = lir->offset + 4;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 4;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (delta & 0x3) {
LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta;
@@ -619,8 +619,8 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
lir->operands[2] = delta >> 2;
}
} else if (lir->opcode == kMipsJal) {
- uintptr_t cur_pc = (start_addr + lir->offset + 4) & ~3;
- uintptr_t target = lir->operands[0];
+ CodeOffset cur_pc = (start_addr + lir->offset + 4) & ~3;
+ CodeOffset target = lir->operands[0];
/* ensure PC-region branch can be used */
DCHECK_EQ((cur_pc & 0xF0000000), (target & 0xF0000000));
if (target & 0x3) {
@@ -629,11 +629,11 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
lir->operands[0] = target >> 2;
} else if (lir->opcode == kMipsLahi) { /* ld address hi (via lui) */
LIR *target_lir = lir->target;
- uintptr_t target = start_addr + target_lir->offset;
+ CodeOffset target = start_addr + target_lir->offset;
lir->operands[1] = target >> 16;
} else if (lir->opcode == kMipsLalo) { /* ld address lo (via ori) */
LIR *target_lir = lir->target;
- uintptr_t target = start_addr + target_lir->offset;
+ CodeOffset target = start_addr + target_lir->offset;
lir->operands[2] = lir->operands[2] + target;
}
}
@@ -646,6 +646,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
if (res != kSuccess) {
continue;
}
+ DCHECK(!IsPseudoLirOp(lir->opcode));
const MipsEncodingMap *encoder = &EncodingMap[lir->opcode];
uint32_t bits = encoder->skeleton;
int i;
@@ -695,6 +696,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
code_buffer_.push_back((bits >> 24) & 0xff);
// TUNING: replace with proper delay slot handling
if (encoder->size == 8) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
const MipsEncodingMap *encoder = &EncodingMap[kMipsNop];
uint32_t bits = encoder->skeleton;
code_buffer_.push_back(bits & 0xff);
@@ -707,7 +709,105 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(uintptr_t start_addr) {
}
int MipsMir2Lir::GetInsnSize(LIR* lir) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
return EncodingMap[lir->opcode].size;
}
+// LIR offset assignment.
+// TODO: consolidate w/ Arm assembly mechanism.
+int MipsMir2Lir::AssignInsnOffsets() {
+ LIR* lir;
+ int offset = 0;
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (LIKELY(lir->opcode >= 0)) {
+ if (!lir->flags.is_nop) {
+ offset += lir->flags.size;
+ }
+ } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) {
+ if (offset & 0x2) {
+ offset += 2;
+ lir->operands[0] = 1;
+ } else {
+ lir->operands[0] = 0;
+ }
+ }
+ /* Pseudo opcodes don't consume space */
+ }
+ return offset;
+}
+
+/*
+ * Walk the compilation unit and assign offsets to instructions
+ * and literals and compute the total size of the compiled unit.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void MipsMir2Lir::AssignOffsets() {
+ int offset = AssignInsnOffsets();
+
+ /* Const values have to be word aligned */
+ offset = (offset + 3) & ~3;
+
+ /* Set up offsets for literals */
+ data_offset_ = offset;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ offset = AssignFillArrayDataOffset(offset);
+
+ total_size_ = offset;
+}
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void MipsMir2Lir::AssembleLIR() {
+ cu_->NewTimingSplit("Assemble");
+ AssignOffsets();
+ int assembler_retries = 0;
+ /*
+ * Assemble here. Note that we generate code with optimistic assumptions
+ * and if found now to work, we'll have to redo the sequence and retry.
+ */
+
+ while (true) {
+ AssemblerStatus res = AssembleInstructions(0);
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ // Redo offsets and try again
+ AssignOffsets();
+ code_buffer_.clear();
+ }
+ }
+
+ // Install literals
+ cu_->NewTimingSplit("LiteralData");
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ cu_->NewTimingSplit("PcMappingTable");
+ CreateMappingTables();
+
+ cu_->NewTimingSplit("GcMap");
+ CreateNativeGcMap();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index d53c012466..18c8cf87f2 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -59,14 +59,14 @@ void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
* done:
*
*/
-void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+void MipsMir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
// Add the table to the list - we'll process it later
- SwitchTable *tab_rec =
+ SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
@@ -101,8 +101,7 @@ void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
// Remember base label so offsets can be computed later
tab_rec->anchor = base_label;
int rBase = AllocTemp();
- NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR4(kMipsDelta, rBase, 0, WrapPointer(base_label), WrapPointer(tab_rec));
OpRegRegReg(kOpAdd, rEnd, rEnd, rBase);
// Grab switch test value
@@ -138,20 +137,20 @@ void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
* jr r_RA
* done:
*/
-void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+void MipsMir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
// Add the table to the list - we'll process it later
- SwitchTable *tab_rec =
+ SwitchTable* tab_rec =
static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int size = table[1];
tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
- ArenaAllocator::kAllocLIR));
+ ArenaAllocator::kAllocLIR));
switch_tables_.Insert(tab_rec);
// Get the switch value
@@ -196,8 +195,7 @@ void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
// Materialize the table base pointer
int rBase = AllocTemp();
- NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR4(kMipsDelta, rBase, 0, WrapPointer(base_label), WrapPointer(tab_rec));
// Load the displacement from the switch table
int r_disp = AllocTemp();
@@ -222,10 +220,10 @@ void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
+void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
// Add the table to the list - we'll process it later
- FillArrayData *tab_rec =
+ FillArrayData* tab_rec =
reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
ArenaAllocator::kAllocData));
tab_rec->table = table;
@@ -252,8 +250,7 @@ void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
LIR* base_label = NewLIR0(kPseudoTargetLabel);
// Materialize a pointer to the fill data image
- NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(base_label),
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR4(kMipsDelta, rMIPS_ARG1, 0, WrapPointer(base_label), WrapPointer(tab_rec));
// And go...
ClobberCalleeSave();
@@ -261,36 +258,6 @@ void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
MarkSafepointPC(call_inst);
}
-/*
- * TODO: implement fast path to short-circuit thin-lock case
- */
-void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
- // Go expensive route - artLockObjectFromCode(self, obj);
- int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pLockObject));
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, r_tgt);
- MarkSafepointPC(call_inst);
-}
-
-/*
- * TODO: implement fast path to short-circuit thin-lock case
- */
-void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rMIPS_ARG0); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
- // Go expensive route - UnlockObjectFromCode(obj);
- int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pUnlockObject));
- ClobberCalleeSave();
- LIR* call_inst = OpReg(kOpBlx, r_tgt);
- MarkSafepointPC(call_inst);
-}
-
void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
int ex_offset = Thread::ExceptionOffset().Int32Value();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
@@ -318,6 +285,7 @@ void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) {
FreeTemp(reg_card_base);
FreeTemp(reg_card_no);
}
+
void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
int spill_count = num_core_spills_ + num_fp_spills_;
/*
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index b9cb720962..88b244ba90 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -52,7 +52,6 @@ class MipsMir2Lir : public Mir2Lir {
int AllocTypedTempPair(bool fp_hint, int reg_class);
int S2d(int low_reg, int high_reg);
int TargetReg(SpecialTargetRegister reg);
- RegisterInfo* GetRegInfo(int reg);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -72,9 +71,12 @@ class MipsMir2Lir : public Mir2Lir {
void CompilerInitializeRegAlloc();
// Required for target - miscellaneous.
- AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void AssembleLIR();
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ AssemblerStatus AssembleInstructions(CodeOffset start_addr);
void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
- void SetupTargetResourceMasks(LIR* lir);
+ void SetupTargetResourceMasks(LIR* lir, uint64_t flags);
const char* GetTargetInstFmt(int opcode);
const char* GetTargetInstName(int opcode);
std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
@@ -86,12 +88,10 @@ class MipsMir2Lir : public Mir2Lir {
// Required for target - Dalvik-level generators.
void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2);
- void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
- RegLocation rl_src, int scale);
void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale);
+ RegLocation rl_index, RegLocation rl_dest, int scale);
void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark);
void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_shift);
void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -107,6 +107,8 @@ class MipsMir2Lir : public Mir2Lir {
bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
bool GenInlinedSqrt(CallInfo* info);
+ bool GenInlinedPeek(CallInfo* info, OpSize size);
+ bool GenInlinedPoke(CallInfo* info, OpSize size);
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -124,8 +126,6 @@ class MipsMir2Lir : public Mir2Lir {
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
void GenMemBarrier(MemBarrierKind barrier_kind);
- void GenMonitorEnter(int opt_flags, RegLocation rl_src);
- void GenMonitorExit(int opt_flags, RegLocation rl_src);
void GenMoveException(RegLocation rl_dest);
void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
int first_bit, int second_bit);
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index 6ce5750a5f..52294290c9 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -268,6 +268,37 @@ bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
return false;
}
+bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ if (size != kSignedByte) {
+ // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
+ return false;
+ }
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ DCHECK(size == kSignedByte);
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
+ if (size != kSignedByte) {
+ // MIPS supports only aligned access. Defer unaligned access to JNI implementation.
+ return false;
+ }
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_src_value = info->args[2]; // [size] value
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ DCHECK(size == kSignedByte);
+ RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
+ return true;
+}
+
LIR* MipsMir2Lir::OpPcRelLoad(int reg, LIR* target) {
LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips";
return NULL;
@@ -484,7 +515,7 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
*
*/
void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -498,12 +529,14 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
rl_array = LoadValue(rl_array, kCoreReg);
rl_index = LoadValue(rl_index, kCoreReg);
int reg_ptr = INVALID_REG;
- if (IsTemp(rl_array.low_reg)) {
+ bool allocated_reg_ptr_temp = false;
+ if (IsTemp(rl_array.low_reg) && !card_mark) {
Clobber(rl_array.low_reg);
reg_ptr = rl_array.low_reg;
} else {
reg_ptr = AllocTemp();
OpRegCopy(reg_ptr, rl_array.low_reg);
+ allocated_reg_ptr_temp = true;
}
/* null object? */
@@ -538,8 +571,6 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
}
StoreBaseDispWide(reg_ptr, 0, rl_src.low_reg, rl_src.high_reg);
-
- FreeTemp(reg_ptr);
} else {
rl_src = LoadValue(rl_src, reg_class);
if (needs_range_check) {
@@ -549,65 +580,11 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
StoreBaseIndexed(reg_ptr, rl_index.low_reg, rl_src.low_reg,
scale, size);
}
-}
-
-/*
- * Generate array store
- *
- */
-void MipsMir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
- int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
-
- FlushAllRegs(); // Use explicit registers
- LockCallTemps();
-
- int r_value = TargetReg(kArg0); // Register holding value
- int r_array_class = TargetReg(kArg1); // Register holding array's Class
- int r_array = TargetReg(kArg2); // Register holding array
- int r_index = TargetReg(kArg3); // Register holding index into array
-
- LoadValueDirectFixed(rl_array, r_array); // Grab array
- LoadValueDirectFixed(rl_src, r_value); // Grab value
- LoadValueDirectFixed(rl_index, r_index); // Grab index
-
- GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
-
- // Store of null?
- LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
-
- // Get the array's class.
- LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
- r_array_class, true);
- // Redo LoadValues in case they didn't survive the call.
- LoadValueDirectFixed(rl_array, r_array); // Reload array
- LoadValueDirectFixed(rl_index, r_index); // Reload index
- LoadValueDirectFixed(rl_src, r_value); // Reload value
- r_array_class = INVALID_REG;
-
- // Branch here if value to be stored == null
- LIR* target = NewLIR0(kPseudoTargetLabel);
- null_value_check->target = target;
-
- bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK));
- int reg_len = INVALID_REG;
- if (needs_range_check) {
- reg_len = TargetReg(kArg1);
- LoadWordDisp(r_array, len_offset, reg_len); // Get len
- }
- /* r_ptr -> array data */
- int r_ptr = AllocTemp();
- OpRegRegImm(kOpAdd, r_ptr, r_array, data_offset);
- if (needs_range_check) {
- GenRegRegCheck(kCondCs, r_index, reg_len, kThrowArrayBounds);
+ if (allocated_reg_ptr_temp) {
+ FreeTemp(reg_ptr);
}
- StoreBaseIndexed(r_ptr, r_index, r_value, scale, kWord);
- FreeTemp(r_ptr);
- FreeTemp(r_index);
- if (!mir_graph_->IsConstantNullRef(rl_src)) {
- MarkGCCard(r_value, r_array);
+ if (card_mark) {
+ MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc
index 4ee5b23eb9..9c598e6bee 100644
--- a/compiler/dex/quick/mips/target_mips.cc
+++ b/compiler/dex/quick/mips/target_mips.cc
@@ -76,6 +76,8 @@ int MipsMir2Lir::TargetReg(SpecialTargetRegister reg) {
case kRet0: res = rMIPS_RET0; break;
case kRet1: res = rMIPS_RET1; break;
case kInvokeTgt: res = rMIPS_INVOKE_TGT; break;
+ case kHiddenArg: res = r_T0; break;
+ case kHiddenFpArg: res = INVALID_REG; break;
case kCount: res = rMIPS_COUNT; break;
}
return res;
@@ -120,22 +122,21 @@ uint64_t MipsMir2Lir::GetPCUseDefEncoding() {
}
-void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir) {
+void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) {
DCHECK_EQ(cu_->instruction_set, kMips);
+ DCHECK(!lir->flags.use_def_invalid);
// Mips-specific resource map setup here.
- uint64_t flags = MipsMir2Lir::EncodingMap[lir->opcode].flags;
-
if (flags & REG_DEF_SP) {
- lir->def_mask |= ENCODE_MIPS_REG_SP;
+ lir->u.m.def_mask |= ENCODE_MIPS_REG_SP;
}
if (flags & REG_USE_SP) {
- lir->use_mask |= ENCODE_MIPS_REG_SP;
+ lir->u.m.use_mask |= ENCODE_MIPS_REG_SP;
}
if (flags & REG_DEF_LR) {
- lir->def_mask |= ENCODE_MIPS_REG_LR;
+ lir->u.m.def_mask |= ENCODE_MIPS_REG_LR;
}
}
@@ -269,8 +270,8 @@ void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, uint64_t mask, const char *pre
}
/* Memory bits */
if (mips_lir && (mask & ENCODE_DALVIK_REG)) {
- sprintf(buf + strlen(buf), "dr%d%s", mips_lir->alias_info & 0xffff,
- (mips_lir->alias_info & 0x80000000) ? "(+1)" : "");
+ sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(mips_lir->flags.alias_info),
+ DECODE_ALIAS_INFO_WIDE(mips_lir->flags.alias_info) ? "(+1)" : "");
}
if (mask & ENCODE_LITERAL) {
strcat(buf, "lit ");
@@ -399,11 +400,6 @@ RegLocation MipsMir2Lir::GetReturnAlt() {
return res;
}
-MipsMir2Lir::RegisterInfo* MipsMir2Lir::GetRegInfo(int reg) {
- return MIPS_FPREG(reg) ? &reg_pool_->FPRegs[reg & MIPS_FP_REG_MASK]
- : &reg_pool_->core_regs[reg];
-}
-
/* To be used when explicitly managing register use */
void MipsMir2Lir::LockCallTemps() {
LockTemp(rMIPS_ARG0);
@@ -559,14 +555,17 @@ Mir2Lir* MipsCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
}
uint64_t MipsMir2Lir::GetTargetInstFlags(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return MipsMir2Lir::EncodingMap[opcode].flags;
}
const char* MipsMir2Lir::GetTargetInstName(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return MipsMir2Lir::EncodingMap[opcode].name;
}
const char* MipsMir2Lir::GetTargetInstFmt(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return MipsMir2Lir::EncodingMap[opcode].fmt;
}
diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc
index 5d9ae33921..2ba2c8487d 100644
--- a/compiler/dex/quick/mips/utility_mips.cc
+++ b/compiler/dex/quick/mips/utility_mips.cc
@@ -93,7 +93,7 @@ LIR* MipsMir2Lir::LoadConstantNoClobber(int r_dest, int value) {
} else if ((value < 0) && (value >= -32768)) {
res = NewLIR3(kMipsAddiu, r_dest, r_ZERO, value);
} else {
- res = NewLIR2(kMipsLui, r_dest, value>>16);
+ res = NewLIR2(kMipsLui, r_dest, value >> 16);
if (value & 0xffff)
NewLIR3(kMipsOri, r_dest, r_dest, value);
}
diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h
index 440df2afa6..1a30b7aef0 100644
--- a/compiler/dex/quick/mir_to_lir-inl.h
+++ b/compiler/dex/quick/mir_to_lir-inl.h
@@ -33,12 +33,17 @@ inline void Mir2Lir::ClobberBody(RegisterInfo* p) {
p->def_end = NULL;
if (p->pair) {
p->pair = false;
- Clobber(p->partner);
+ p = GetRegInfo(p->partner);
+ p->pair = false;
+ p->live = false;
+ p->s_reg = INVALID_SREG;
+ p->def_start = NULL;
+ p->def_end = NULL;
}
}
}
-inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0,
+inline LIR* Mir2Lir::RawLIR(DexOffset dalvik_offset, int opcode, int op0,
int op1, int op2, int op3, int op4, LIR* target) {
LIR* insn = static_cast<LIR*>(arena_->Alloc(sizeof(LIR), ArenaAllocator::kAllocLIR));
insn->dalvik_offset = dalvik_offset;
@@ -53,7 +58,8 @@ inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0,
if ((opcode == kPseudoTargetLabel) || (opcode == kPseudoSafepointPC) ||
(opcode == kPseudoExportedPC)) {
// Always make labels scheduling barriers
- insn->use_mask = insn->def_mask = ENCODE_ALL;
+ DCHECK(!insn->flags.use_def_invalid);
+ insn->u.m.use_mask = insn->u.m.def_mask = ENCODE_ALL;
}
return insn;
}
@@ -63,7 +69,7 @@ inline LIR* Mir2Lir::RawLIR(int dalvik_offset, int opcode, int op0,
* operands.
*/
inline LIR* Mir2Lir::NewLIR0(int opcode) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & NO_OPERAND))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -73,7 +79,7 @@ inline LIR* Mir2Lir::NewLIR0(int opcode) {
}
inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_UNARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -83,7 +89,7 @@ inline LIR* Mir2Lir::NewLIR1(int opcode, int dest) {
}
inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_BINARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -93,7 +99,7 @@ inline LIR* Mir2Lir::NewLIR2(int opcode, int dest, int src1) {
}
inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_TERTIARY_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -103,7 +109,7 @@ inline LIR* Mir2Lir::NewLIR3(int opcode, int dest, int src1, int src2) {
}
inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_QUAD_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -114,7 +120,7 @@ inline LIR* Mir2Lir::NewLIR4(int opcode, int dest, int src1, int src2, int info)
inline LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1,
int info2) {
- DCHECK(is_pseudo_opcode(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP))
+ DCHECK(IsPseudoLirOp(opcode) || (GetTargetInstFlags(opcode) & IS_QUIN_OP))
<< GetTargetInstName(opcode) << " " << opcode << " "
<< PrettyMethod(cu_->method_idx, *cu_->dex_file) << " "
<< current_dalvik_offset_;
@@ -136,20 +142,23 @@ inline void Mir2Lir::SetupRegMask(uint64_t* mask, int reg) {
inline void Mir2Lir::SetupResourceMasks(LIR* lir) {
int opcode = lir->opcode;
- if (opcode <= 0) {
- lir->use_mask = lir->def_mask = 0;
+ if (IsPseudoLirOp(opcode)) {
+ if (opcode != kPseudoBarrier) {
+ lir->flags.fixup = kFixupLabel;
+ }
return;
}
uint64_t flags = GetTargetInstFlags(opcode);
if (flags & NEEDS_FIXUP) {
- lir->flags.pcRelFixup = true;
+ // Note: target-specific setup may specialize the fixup kind.
+ lir->flags.fixup = kFixupLabel;
}
/* Get the starting size of the instruction's template */
lir->flags.size = GetInsnSize(lir);
-
+ estimated_native_code_size_ += lir->flags.size;
/* Set up the mask for resources that are updated */
if (flags & (IS_LOAD | IS_STORE)) {
/* Default to heap - will catch specialized classes later */
@@ -161,39 +170,49 @@ inline void Mir2Lir::SetupResourceMasks(LIR* lir) {
* turn will trash everything.
*/
if (flags & IS_BRANCH) {
- lir->def_mask = lir->use_mask = ENCODE_ALL;
+ lir->u.m.def_mask = lir->u.m.use_mask = ENCODE_ALL;
return;
}
if (flags & REG_DEF0) {
- SetupRegMask(&lir->def_mask, lir->operands[0]);
+ SetupRegMask(&lir->u.m.def_mask, lir->operands[0]);
}
if (flags & REG_DEF1) {
- SetupRegMask(&lir->def_mask, lir->operands[1]);
+ SetupRegMask(&lir->u.m.def_mask, lir->operands[1]);
}
+ if (flags & REG_USE0) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[0]);
+ }
- if (flags & SETS_CCODES) {
- lir->def_mask |= ENCODE_CCODE;
+ if (flags & REG_USE1) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[1]);
+ }
+
+ if (flags & REG_USE2) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[2]);
}
- if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
- int i;
+ if (flags & REG_USE3) {
+ SetupRegMask(&lir->u.m.use_mask, lir->operands[3]);
+ }
- for (i = 0; i < 4; i++) {
- if (flags & (1 << (kRegUse0 + i))) {
- SetupRegMask(&lir->use_mask, lir->operands[i]);
- }
- }
+ if (flags & SETS_CCODES) {
+ lir->u.m.def_mask |= ENCODE_CCODE;
}
if (flags & USES_CCODES) {
- lir->use_mask |= ENCODE_CCODE;
+ lir->u.m.use_mask |= ENCODE_CCODE;
}
// Handle target-specific actions
- SetupTargetResourceMasks(lir);
+ SetupTargetResourceMasks(lir, flags);
+}
+
+inline art::Mir2Lir::RegisterInfo* Mir2Lir::GetRegInfo(int reg) {
+ DCHECK(reginfo_map_.Get(reg) != NULL);
+ return reginfo_map_.Get(reg);
}
} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index c41feb1348..fa9a3ad566 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -18,6 +18,7 @@
#include "dex/dataflow_iterator-inl.h"
#include "mir_to_lir-inl.h"
#include "object_utils.h"
+#include "thread-inl.h"
namespace art {
@@ -240,9 +241,9 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
case Instruction::GOTO_16:
case Instruction::GOTO_32:
if (mir_graph_->IsBackedge(bb, bb->taken)) {
- GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken->id]);
+ GenSuspendTestAndBranch(opt_flags, &label_list[bb->taken]);
} else {
- OpUnconditionalBranch(&label_list[bb->taken->id]);
+ OpUnconditionalBranch(&label_list[bb->taken]);
}
break;
@@ -271,23 +272,22 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
case Instruction::IF_GE:
case Instruction::IF_GT:
case Instruction::IF_LE: {
- LIR* taken = &label_list[bb->taken->id];
- LIR* fall_through = &label_list[bb->fall_through->id];
+ LIR* taken = &label_list[bb->taken];
+ LIR* fall_through = &label_list[bb->fall_through];
// Result known at compile time?
if (rl_src[0].is_const && rl_src[1].is_const) {
bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg),
mir_graph_->ConstantValue(rl_src[1].orig_sreg));
- BasicBlock* target = is_taken ? bb->taken : bb->fall_through;
- if (mir_graph_->IsBackedge(bb, target)) {
+ BasicBlockId target_id = is_taken ? bb->taken : bb->fall_through;
+ if (mir_graph_->IsBackedge(bb, target_id)) {
GenSuspendTest(opt_flags);
}
- OpUnconditionalBranch(&label_list[target->id]);
+ OpUnconditionalBranch(&label_list[target_id]);
} else {
if (mir_graph_->IsBackwardsBranch(bb)) {
GenSuspendTest(opt_flags);
}
- GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken,
- fall_through);
+ GenCompareAndBranch(opcode, rl_src[0], rl_src[1], taken, fall_through);
}
break;
}
@@ -298,16 +298,16 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
case Instruction::IF_GEZ:
case Instruction::IF_GTZ:
case Instruction::IF_LEZ: {
- LIR* taken = &label_list[bb->taken->id];
- LIR* fall_through = &label_list[bb->fall_through->id];
+ LIR* taken = &label_list[bb->taken];
+ LIR* fall_through = &label_list[bb->fall_through];
// Result known at compile time?
if (rl_src[0].is_const) {
bool is_taken = EvaluateBranch(opcode, mir_graph_->ConstantValue(rl_src[0].orig_sreg), 0);
- BasicBlock* target = is_taken ? bb->taken : bb->fall_through;
- if (mir_graph_->IsBackedge(bb, target)) {
+ BasicBlockId target_id = is_taken ? bb->taken : bb->fall_through;
+ if (mir_graph_->IsBackedge(bb, target_id)) {
GenSuspendTest(opt_flags);
}
- OpUnconditionalBranch(&label_list[target->id]);
+ OpUnconditionalBranch(&label_list[target_id]);
} else {
if (mir_graph_->IsBackwardsBranch(bb)) {
GenSuspendTest(opt_flags);
@@ -337,22 +337,35 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list
GenArrayGet(opt_flags, kSignedHalf, rl_src[0], rl_src[1], rl_dest, 1);
break;
case Instruction::APUT_WIDE:
- GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3);
+ GenArrayPut(opt_flags, kLong, rl_src[1], rl_src[2], rl_src[0], 3, false);
break;
case Instruction::APUT:
- GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2);
- break;
- case Instruction::APUT_OBJECT:
- GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0], 2);
+ GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, false);
+ break;
+ case Instruction::APUT_OBJECT: {
+ bool is_null = mir_graph_->IsConstantNullRef(rl_src[0]);
+ bool is_safe = is_null; // Always safe to store null.
+ if (!is_safe) {
+ // Check safety from verifier type information.
+ const MethodReference mr(cu_->dex_file, cu_->method_idx);
+ is_safe = cu_->compiler_driver->IsSafeCast(mr, mir->offset);
+ }
+ if (is_null || is_safe) {
+ // Store of constant null doesn't require an assignability test and can be generated inline
+ // without fixed register usage or a card mark.
+ GenArrayPut(opt_flags, kWord, rl_src[1], rl_src[2], rl_src[0], 2, !is_null);
+ } else {
+ GenArrayObjPut(opt_flags, rl_src[1], rl_src[2], rl_src[0]);
+ }
break;
+ }
case Instruction::APUT_SHORT:
case Instruction::APUT_CHAR:
- GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1);
+ GenArrayPut(opt_flags, kUnsignedHalf, rl_src[1], rl_src[2], rl_src[0], 1, false);
break;
case Instruction::APUT_BYTE:
case Instruction::APUT_BOOLEAN:
- GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2],
- rl_src[0], 0);
+ GenArrayPut(opt_flags, kUnsignedByte, rl_src[1], rl_src[2], rl_src[0], 0, false);
break;
case Instruction::IGET_OBJECT:
@@ -696,6 +709,7 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {
// Insert the block label.
block_label_list_[block_id].opcode = kPseudoNormalBlockLabel;
+ block_label_list_[block_id].flags.fixup = kFixupLabel;
AppendLIR(&block_label_list_[block_id]);
LIR* head_lir = NULL;
@@ -706,16 +720,15 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {
}
// Free temp registers and reset redundant store tracking.
- ResetRegPool();
- ResetDefTracking();
-
ClobberAllRegs();
if (bb->block_type == kEntryBlock) {
+ ResetRegPool();
int start_vreg = cu_->num_dalvik_registers - cu_->num_ins;
GenEntrySequence(&mir_graph_->reg_location_[start_vreg],
mir_graph_->reg_location_[mir_graph_->GetMethodSReg()]);
} else if (bb->block_type == kExitBlock) {
+ ResetRegPool();
GenExitSequence();
}
@@ -736,17 +749,18 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {
current_dalvik_offset_ = mir->offset;
int opcode = mir->dalvikInsn.opcode;
- LIR* boundary_lir;
// Mark the beginning of a Dalvik instruction for line tracking.
- char* inst_str = cu_->verbose ?
- mir_graph_->GetDalvikDisassembly(mir) : NULL;
- boundary_lir = MarkBoundary(mir->offset, inst_str);
+ if (cu_->verbose) {
+ char* inst_str = mir_graph_->GetDalvikDisassembly(mir);
+ MarkBoundary(mir->offset, inst_str);
+ }
// Remember the first LIR for this block.
if (head_lir == NULL) {
- head_lir = boundary_lir;
- // Set the first boundary_lir as a scheduling barrier.
- head_lir->def_mask = ENCODE_ALL;
+ head_lir = &block_label_list_[bb->id];
+ // Set the first label as a scheduling barrier.
+ DCHECK(!head_lir->flags.use_def_invalid);
+ head_lir->u.m.def_mask = ENCODE_ALL;
}
if (opcode == kMirOpCheck) {
@@ -771,11 +785,6 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) {
if (head_lir) {
// Eliminate redundant loads/stores and delay stores into later slots.
ApplyLocalOptimizations(head_lir, last_lir_insn_);
-
- // Generate an unconditional branch to the fallthrough block.
- if (bb->fall_through) {
- OpUnconditionalBranch(&block_label_list_[bb->fall_through->id]);
- }
}
return false;
}
@@ -810,25 +819,34 @@ void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) {
}
void Mir2Lir::MethodMIR2LIR() {
+ cu_->NewTimingSplit("MIR2LIR");
+
// Hold the labels of each block.
block_label_list_ =
static_cast<LIR*>(arena_->Alloc(sizeof(LIR) * mir_graph_->GetNumBlocks(),
ArenaAllocator::kAllocLIR));
- PreOrderDfsIterator iter(mir_graph_, false /* not iterative */);
- for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
- MethodBlockCodeGen(bb);
+ PreOrderDfsIterator iter(mir_graph_);
+ BasicBlock* curr_bb = iter.Next();
+ BasicBlock* next_bb = iter.Next();
+ while (curr_bb != NULL) {
+ MethodBlockCodeGen(curr_bb);
+ // If the fall_through block is no longer laid out consecutively, drop in a branch.
+ BasicBlock* curr_bb_fall_through = mir_graph_->GetBasicBlock(curr_bb->fall_through);
+ if ((curr_bb_fall_through != NULL) && (curr_bb_fall_through != next_bb)) {
+ OpUnconditionalBranch(&block_label_list_[curr_bb->fall_through]);
+ }
+ curr_bb = next_bb;
+ do {
+ next_bb = iter.Next();
+ } while ((next_bb != NULL) && (next_bb->block_type == kDead));
}
-
+ cu_->NewTimingSplit("Launchpads");
HandleSuspendLaunchPads();
HandleThrowLaunchPads();
HandleIntrinsicLaunchPads();
-
- if (!(cu_->disable_opt & (1 << kSafeOptimizations))) {
- RemoveRedundantBranches();
- }
}
} // namespace art
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index a37ebd173f..4c56b74dc4 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -30,6 +30,14 @@
namespace art {
+/*
+ * TODO: refactoring pass to move these (and other) typdefs towards usage style of runtime to
+ * add type safety (see runtime/offsets.h).
+ */
+typedef uint32_t DexOffset; // Dex offset in code units.
+typedef uint16_t NarrowDexOffset; // For use in structs, Dex offsets range from 0 .. 0xffff.
+typedef uint32_t CodeOffset; // Native code offset in bytes.
+
// Set to 1 to measure cost of suspend check.
#define NO_SUSPEND 0
@@ -95,6 +103,7 @@ struct BasicBlock;
struct CallInfo;
struct CompilationUnit;
struct MIR;
+struct LIR;
struct RegLocation;
struct RegisterInfo;
class MIRGraph;
@@ -107,24 +116,36 @@ typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int,
typedef std::vector<uint8_t> CodeBuffer;
+struct UseDefMasks {
+ uint64_t use_mask; // Resource mask for use.
+ uint64_t def_mask; // Resource mask for def.
+};
+
+struct AssemblyInfo {
+ LIR* pcrel_next; // Chain of LIR nodes needing pc relative fixups.
+ uint8_t bytes[16]; // Encoded instruction bytes.
+};
struct LIR {
- int offset; // Offset of this instruction.
- int dalvik_offset; // Offset of Dalvik opcode.
+ CodeOffset offset; // Offset of this instruction.
+ NarrowDexOffset dalvik_offset; // Offset of Dalvik opcode in code units (16-bit words).
+ int16_t opcode;
LIR* next;
LIR* prev;
LIR* target;
- int opcode;
- int operands[5]; // [0..4] = [dest, src1, src2, extra, extra2].
struct {
- bool is_nop:1; // LIR is optimized away.
- bool pcRelFixup:1; // May need pc-relative fixup.
- unsigned int size:5; // Note: size is in bytes.
- unsigned int unused:25;
+ unsigned int alias_info:17; // For Dalvik register disambiguation.
+ bool is_nop:1; // LIR is optimized away.
+ unsigned int size:4; // Note: size of encoded instruction is in bytes.
+ bool use_def_invalid:1; // If true, masks should not be used.
+ unsigned int generation:1; // Used to track visitation state during fixup pass.
+ unsigned int fixup:8; // Fixup kind.
} flags;
- int alias_info; // For Dalvik register & litpool disambiguation.
- uint64_t use_mask; // Resource mask for use.
- uint64_t def_mask; // Resource mask for def.
+ union {
+ UseDefMasks m; // Use & Def masks used during optimization.
+ AssemblyInfo a; // Instruction encoding used during assembly phase.
+ } u;
+ int32_t operands[5]; // [0..4] = [dest, src1, src2, extra, extra2].
};
// Target-specific initialization.
@@ -141,7 +162,7 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
// Defines for alias_info (tracks Dalvik register references).
#define DECODE_ALIAS_INFO_REG(X) (X & 0xffff)
-#define DECODE_ALIAS_INFO_WIDE_FLAG (0x80000000)
+#define DECODE_ALIAS_INFO_WIDE_FLAG (0x10000)
#define DECODE_ALIAS_INFO_WIDE(X) ((X & DECODE_ALIAS_INFO_WIDE_FLAG) ? 1 : 0)
#define ENCODE_ALIAS_INFO(REG, ISWIDE) (REG | (ISWIDE ? DECODE_ALIAS_INFO_WIDE_FLAG : 0))
@@ -158,36 +179,42 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
#define ENCODE_ALL (~0ULL)
#define ENCODE_MEM (ENCODE_DALVIK_REG | ENCODE_LITERAL | \
ENCODE_HEAP_REF | ENCODE_MUST_NOT_ALIAS)
+
+// Mask to denote sreg as the start of a double. Must not interfere with low 16 bits.
+#define STARTING_DOUBLE_SREG 0x10000
+
// TODO: replace these macros
#define SLOW_FIELD_PATH (cu_->enable_debug & (1 << kDebugSlowFieldPath))
#define SLOW_INVOKE_PATH (cu_->enable_debug & (1 << kDebugSlowInvokePath))
#define SLOW_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowStringPath))
#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
-#define is_pseudo_opcode(opcode) (static_cast<int>(opcode) < 0)
class Mir2Lir : public Backend {
public:
- struct SwitchTable {
- int offset;
- const uint16_t* table; // Original dex table.
- int vaddr; // Dalvik offset of switch opcode.
- LIR* anchor; // Reference instruction for relative offsets.
- LIR** targets; // Array of case targets.
+ /*
+ * Auxiliary information describing the location of data embedded in the Dalvik
+ * byte code stream.
+ */
+ struct EmbeddedData {
+ CodeOffset offset; // Code offset of data block.
+ const uint16_t* table; // Original dex data.
+ DexOffset vaddr; // Dalvik offset of parent opcode.
};
- struct FillArrayData {
- int offset;
- const uint16_t* table; // Original dex table.
- int size;
- int vaddr; // Dalvik offset of FILL_ARRAY_DATA opcode.
+ struct FillArrayData : EmbeddedData {
+ int32_t size;
+ };
+
+ struct SwitchTable : EmbeddedData {
+ LIR* anchor; // Reference instruction for relative offsets.
+ LIR** targets; // Array of case targets.
};
/* Static register use counts */
struct RefCounts {
int count;
int s_reg;
- bool double_start; // Starting v_reg for a double
};
/*
@@ -241,6 +268,38 @@ class Mir2Lir : public Backend {
return code_buffer_.size() / sizeof(code_buffer_[0]);
}
+ bool IsPseudoLirOp(int opcode) {
+ return (opcode < 0);
+ }
+
+ /*
+ * LIR operands are 32-bit integers. Sometimes, (especially for managing
+ * instructions which require PC-relative fixups), we need the operands to carry
+ * pointers. To do this, we assign these pointers an index in pointer_storage_, and
+ * hold that index in the operand array.
+ * TUNING: If use of these utilities becomes more common on 32-bit builds, it
+ * may be worth conditionally-compiling a set of identity functions here.
+ */
+ uint32_t WrapPointer(void* pointer) {
+ uint32_t res = pointer_storage_.Size();
+ pointer_storage_.Insert(pointer);
+ return res;
+ }
+
+ void* UnwrapPointer(size_t index) {
+ return pointer_storage_.Get(index);
+ }
+
+ // strdup(), but allocates from the arena.
+ char* ArenaStrdup(const char* str) {
+ size_t len = strlen(str) + 1;
+ char* res = reinterpret_cast<char*>(arena_->Alloc(len, ArenaAllocator::kAllocMisc));
+ if (res != NULL) {
+ strncpy(res, str, len);
+ }
+ return res;
+ }
+
// Shared by all targets - implemented in codegen_util.cc
void AppendLIR(LIR* lir);
void InsertLIRBefore(LIR* current_lir, LIR* new_lir);
@@ -250,16 +309,15 @@ class Mir2Lir : public Backend {
virtual void Materialize();
virtual CompiledMethod* GetCompiledMethod();
void MarkSafepointPC(LIR* inst);
- bool FastInstance(uint32_t field_idx, int& field_offset, bool& is_volatile, bool is_put);
+ bool FastInstance(uint32_t field_idx, bool is_put, int* field_offset, bool* is_volatile);
void SetupResourceMasks(LIR* lir);
- void AssembleLIR();
void SetMemRefType(LIR* lir, bool is_load, int mem_type);
void AnnotateDalvikRegAccess(LIR* lir, int reg_id, bool is_load, bool is64bit);
void SetupRegMask(uint64_t* mask, int reg);
void DumpLIRInsn(LIR* arg, unsigned char* base_addr);
void DumpPromotionMap();
void CodegenDump();
- LIR* RawLIR(int dalvik_offset, int opcode, int op0 = 0, int op1 = 0,
+ LIR* RawLIR(DexOffset dalvik_offset, int opcode, int op0 = 0, int op1 = 0,
int op2 = 0, int op3 = 0, int op4 = 0, LIR* target = NULL);
LIR* NewLIR0(int opcode);
LIR* NewLIR1(int opcode, int dest);
@@ -274,13 +332,14 @@ class Mir2Lir : public Backend {
void ProcessSwitchTables();
void DumpSparseSwitchTable(const uint16_t* table);
void DumpPackedSwitchTable(const uint16_t* table);
- LIR* MarkBoundary(int offset, const char* inst_str);
+ void MarkBoundary(DexOffset offset, const char* inst_str);
void NopLIR(LIR* lir);
+ void UnlinkLIR(LIR* lir);
bool EvaluateBranch(Instruction::Code opcode, int src1, int src2);
bool IsInexpensiveConstant(RegLocation rl_src);
ConditionCode FlipComparisonOrder(ConditionCode before);
- void DumpMappingTable(const char* table_name, const std::string& descriptor,
- const std::string& name, const std::string& signature,
+ void DumpMappingTable(const char* table_name, const char* descriptor,
+ const char* name, const Signature& signature,
const std::vector<uint32_t>& v);
void InstallLiteralPools();
void InstallSwitchTables();
@@ -288,21 +347,18 @@ class Mir2Lir : public Backend {
bool VerifyCatchEntries();
void CreateMappingTables();
void CreateNativeGcMap();
- int AssignLiteralOffset(int offset);
- int AssignSwitchTablesOffset(int offset);
- int AssignFillArrayDataOffset(int offset);
- int AssignInsnOffsets();
- void AssignOffsets();
- LIR* InsertCaseLabel(int vaddr, int keyVal);
- void MarkPackedCaseLabels(Mir2Lir::SwitchTable *tab_rec);
- void MarkSparseCaseLabels(Mir2Lir::SwitchTable *tab_rec);
+ int AssignLiteralOffset(CodeOffset offset);
+ int AssignSwitchTablesOffset(CodeOffset offset);
+ int AssignFillArrayDataOffset(CodeOffset offset);
+ LIR* InsertCaseLabel(DexOffset vaddr, int keyVal);
+ void MarkPackedCaseLabels(Mir2Lir::SwitchTable* tab_rec);
+ void MarkSparseCaseLabels(Mir2Lir::SwitchTable* tab_rec);
// Shared by all targets - implemented in local_optimizations.cc
void ConvertMemOpIntoMove(LIR* orig_lir, int dest, int src);
void ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir);
void ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir);
void ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir);
- void RemoveRedundantBranches();
// Shared by all targets - implemented in ralloc_util.cc
int GetSRegHi(int lowSreg);
@@ -324,11 +380,9 @@ class Mir2Lir : public Backend {
void RecordCorePromotion(int reg, int s_reg);
int AllocPreservedCoreReg(int s_reg);
void RecordFpPromotion(int reg, int s_reg);
- int AllocPreservedSingle(int s_reg, bool even);
+ int AllocPreservedSingle(int s_reg);
int AllocPreservedDouble(int s_reg);
- int AllocPreservedFPReg(int s_reg, bool double_start);
- int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp,
- bool required);
+ int AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp, bool required);
int AllocTempDouble();
int AllocFreeTemp();
int AllocTemp();
@@ -367,13 +421,14 @@ class Mir2Lir : public Backend {
RegLocation UpdateRawLoc(RegLocation loc);
RegLocation EvalLocWide(RegLocation loc, int reg_class, bool update);
RegLocation EvalLoc(RegLocation loc, int reg_class, bool update);
- void CountRefs(RefCounts* core_counts, RefCounts* fp_counts);
+ void CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs);
void DumpCounts(const RefCounts* arr, int size, const char* msg);
void DoPromotion();
int VRegOffset(int v_reg);
int SRegOffset(int s_reg);
RegLocation GetReturnWide(bool is_double);
RegLocation GetReturn(bool is_float);
+ RegisterInfo* GetRegInfo(int reg);
// Shared by all targets - implemented in gen_common.cc.
bool HandleEasyDivRem(Instruction::Code dalvik_opcode, bool is_div,
@@ -407,6 +462,9 @@ class Mir2Lir : public Backend {
RegLocation rl_dest, RegLocation rl_obj, bool is_long_or_double, bool is_object);
void GenIPut(uint32_t field_idx, int opt_flags, OpSize size,
RegLocation rl_src, RegLocation rl_obj, bool is_long_or_double, bool is_object);
+ void GenArrayObjPut(int opt_flags, RegLocation rl_array, RegLocation rl_index,
+ RegLocation rl_src);
+
void GenConstClass(uint32_t type_idx, RegLocation rl_dest);
void GenConstString(uint32_t string_idx, RegLocation rl_dest);
void GenNewInstance(uint32_t type_idx, RegLocation rl_dest);
@@ -463,6 +521,10 @@ class Mir2Lir : public Backend {
void CallRuntimeHelperImmRegLocationRegLocation(ThreadOffset helper_offset,
int arg0, RegLocation arg1, RegLocation arg2,
bool safepoint_pc);
+ void CallRuntimeHelperRegLocationRegLocationRegLocation(ThreadOffset helper_offset,
+ RegLocation arg0, RegLocation arg1,
+ RegLocation arg2,
+ bool safepoint_pc);
void GenInvoke(CallInfo* info);
void FlushIns(RegLocation* ArgLocs, RegLocation rl_method);
int GenDalvikArgsNoRange(CallInfo* info, int call_state, LIR** pcrLabel,
@@ -482,6 +544,7 @@ class Mir2Lir : public Backend {
bool GenInlinedCharAt(CallInfo* info);
bool GenInlinedStringIsEmptyOrLength(CallInfo* info, bool is_empty);
+ bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
bool GenInlinedAbsInt(CallInfo* info);
bool GenInlinedAbsLong(CallInfo* info);
bool GenInlinedFloatCvt(CallInfo* info);
@@ -550,7 +613,6 @@ class Mir2Lir : public Backend {
virtual int AllocTypedTempPair(bool fp_hint, int reg_class) = 0;
virtual int S2d(int low_reg, int high_reg) = 0;
virtual int TargetReg(SpecialTargetRegister reg) = 0;
- virtual RegisterInfo* GetRegInfo(int reg) = 0;
virtual RegLocation GetReturnAlt() = 0;
virtual RegLocation GetReturnWideAlt() = 0;
virtual RegLocation LocCReturn() = 0;
@@ -570,9 +632,9 @@ class Mir2Lir : public Backend {
virtual void CompilerInitializeRegAlloc() = 0;
// Required for target - miscellaneous.
- virtual AssemblerStatus AssembleInstructions(uintptr_t start_addr) = 0;
+ virtual void AssembleLIR() = 0;
virtual void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix) = 0;
- virtual void SetupTargetResourceMasks(LIR* lir) = 0;
+ virtual void SetupTargetResourceMasks(LIR* lir, uint64_t flags) = 0;
virtual const char* GetTargetInstFmt(int opcode) = 0;
virtual const char* GetTargetInstName(int opcode) = 0;
virtual std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr) = 0;
@@ -602,6 +664,8 @@ class Mir2Lir : public Backend {
virtual bool GenInlinedCas32(CallInfo* info, bool need_write_barrier) = 0;
virtual bool GenInlinedMinMaxInt(CallInfo* info, bool is_min) = 0;
virtual bool GenInlinedSqrt(CallInfo* info) = 0;
+ virtual bool GenInlinedPeek(CallInfo* info, OpSize size) = 0;
+ virtual bool GenInlinedPoke(CallInfo* info, OpSize size) = 0;
virtual void GenNegLong(RegLocation rl_dest, RegLocation rl_src) = 0;
virtual void GenOrLong(RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2) = 0;
@@ -621,46 +685,40 @@ class Mir2Lir : public Backend {
virtual void GenEntrySequence(RegLocation* ArgLocs,
RegLocation rl_method) = 0;
virtual void GenExitSequence() = 0;
- virtual void GenFillArrayData(uint32_t table_offset,
+ virtual void GenFillArrayData(DexOffset table_offset,
RegLocation rl_src) = 0;
virtual void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
bool is_double) = 0;
virtual void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) = 0;
virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0;
virtual void GenMemBarrier(MemBarrierKind barrier_kind) = 0;
- virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src) = 0;
- virtual void GenMonitorExit(int opt_flags, RegLocation rl_src) = 0;
virtual void GenMoveException(RegLocation rl_dest) = 0;
virtual void GenMultiplyByTwoBitMultiplier(RegLocation rl_src,
RegLocation rl_result, int lit, int first_bit,
int second_bit) = 0;
virtual void GenNegDouble(RegLocation rl_dest, RegLocation rl_src) = 0;
virtual void GenNegFloat(RegLocation rl_dest, RegLocation rl_src) = 0;
- virtual void GenPackedSwitch(MIR* mir, uint32_t table_offset,
+ virtual void GenPackedSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) = 0;
- virtual void GenSparseSwitch(MIR* mir, uint32_t table_offset,
+ virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) = 0;
virtual void GenSpecialCase(BasicBlock* bb, MIR* mir,
SpecialCaseHandler special_case) = 0;
- virtual void GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) = 0;
virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale) = 0;
virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) = 0;
+ RegLocation rl_index, RegLocation rl_src, int scale,
+ bool card_mark) = 0;
virtual void GenShiftImmOpLong(Instruction::Code opcode,
RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_shift) = 0;
// Required for target - single operation generators.
virtual LIR* OpUnconditionalBranch(LIR* target) = 0;
- virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2,
- LIR* target) = 0;
- virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value,
- LIR* target) = 0;
+ virtual LIR* OpCmpBranch(ConditionCode cond, int src1, int src2, LIR* target) = 0;
+ virtual LIR* OpCmpImmBranch(ConditionCode cond, int reg, int check_value, LIR* target) = 0;
virtual LIR* OpCondBranch(ConditionCode cc, LIR* target) = 0;
- virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg,
- LIR* target) = 0;
+ virtual LIR* OpDecAndBranch(ConditionCode c_code, int reg, LIR* target) = 0;
virtual LIR* OpFpRegCopy(int r_dest, int r_src) = 0;
virtual LIR* OpIT(ConditionCode cond, const char* guide) = 0;
virtual LIR* OpMem(OpKind op, int rBase, int disp) = 0;
@@ -672,22 +730,23 @@ class Mir2Lir : public Backend {
virtual LIR* OpRegMem(OpKind op, int r_dest, int rBase, int offset) = 0;
virtual LIR* OpRegReg(OpKind op, int r_dest_src1, int r_src2) = 0;
virtual LIR* OpRegRegImm(OpKind op, int r_dest, int r_src1, int value) = 0;
- virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1,
- int r_src2) = 0;
+ virtual LIR* OpRegRegReg(OpKind op, int r_dest, int r_src1, int r_src2) = 0;
virtual LIR* OpTestSuspend(LIR* target) = 0;
virtual LIR* OpThreadMem(OpKind op, ThreadOffset thread_offset) = 0;
virtual LIR* OpVldm(int rBase, int count) = 0;
virtual LIR* OpVstm(int rBase, int count) = 0;
- virtual void OpLea(int rBase, int reg1, int reg2, int scale,
- int offset) = 0;
- virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo,
- int src_hi) = 0;
+ virtual void OpLea(int rBase, int reg1, int reg2, int scale, int offset) = 0;
+ virtual void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi) = 0;
virtual void OpTlsCmp(ThreadOffset offset, int val) = 0;
virtual bool InexpensiveConstantInt(int32_t value) = 0;
virtual bool InexpensiveConstantFloat(int32_t value) = 0;
virtual bool InexpensiveConstantLong(int64_t value) = 0;
virtual bool InexpensiveConstantDouble(int64_t value) = 0;
+ // May be optimized by targets.
+ virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src);
+ virtual void GenMonitorExit(int opt_flags, RegLocation rl_src);
+
// Temp workaround
void Workaround7250540(RegLocation rl_dest, int value);
@@ -718,6 +777,7 @@ class Mir2Lir : public Backend {
LIR* literal_list_; // Constants.
LIR* method_literal_list_; // Method literals requiring patching.
LIR* code_literal_list_; // Code literals requiring patching.
+ LIR* first_fixup_; // Doubly-linked list of LIR nodes requiring fixups.
protected:
CompilationUnit* const cu_;
@@ -727,7 +787,9 @@ class Mir2Lir : public Backend {
GrowableArray<LIR*> throw_launchpads_;
GrowableArray<LIR*> suspend_launchpads_;
GrowableArray<LIR*> intrinsic_launchpads_;
- SafeMap<unsigned int, LIR*> boundary_map_; // boundary lookup cache.
+ GrowableArray<RegisterInfo*> tempreg_info_;
+ GrowableArray<RegisterInfo*> reginfo_map_;
+ GrowableArray<void*> pointer_storage_;
/*
* Holds mapping from native PC to dex PC for safepoints where we may deoptimize.
* Native PC is on the return address of the safepointed operation. Dex PC is for
@@ -739,8 +801,9 @@ class Mir2Lir : public Backend {
* immediately preceed the instruction.
*/
std::vector<uint32_t> dex2pc_mapping_table_;
- int data_offset_; // starting offset of literal pool.
- int total_size_; // header + code size.
+ CodeOffset current_code_offset_; // Working byte offset of machine instructons.
+ CodeOffset data_offset_; // starting offset of literal pool.
+ size_t total_size_; // header + code size.
LIR* block_label_list_;
PromotionMap* promotion_map_;
/*
@@ -752,7 +815,8 @@ class Mir2Lir : public Backend {
* in the CompilationUnit struct before codegen for each instruction.
* The low-level LIR creation utilites will pull it from here. Rework this.
*/
- int current_dalvik_offset_;
+ DexOffset current_dalvik_offset_;
+ size_t estimated_native_code_size_; // Just an estimate; used to reserve code_buffer_ size.
RegisterPool* reg_pool_;
/*
* Sanity checking for the register temp tracking. The same ssa
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index 71b74a4a68..41a57afca1 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -28,13 +28,9 @@ namespace art {
* live until it is either explicitly killed or reallocated.
*/
void Mir2Lir::ResetRegPool() {
- for (int i = 0; i < reg_pool_->num_core_regs; i++) {
- if (reg_pool_->core_regs[i].is_temp)
- reg_pool_->core_regs[i].in_use = false;
- }
- for (int i = 0; i < reg_pool_->num_fp_regs; i++) {
- if (reg_pool_->FPRegs[i].is_temp)
- reg_pool_->FPRegs[i].in_use = false;
+ GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
+ for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ info->in_use = false;
}
// Reset temp tracking sanity check.
if (kIsDebugBuild) {
@@ -48,13 +44,21 @@ void Mir2Lir::ResetRegPool() {
*/
void Mir2Lir::CompilerInitPool(RegisterInfo* regs, int* reg_nums, int num) {
for (int i = 0; i < num; i++) {
- regs[i].reg = reg_nums[i];
+ uint32_t reg_number = reg_nums[i];
+ regs[i].reg = reg_number;
regs[i].in_use = false;
regs[i].is_temp = false;
regs[i].pair = false;
regs[i].live = false;
regs[i].dirty = false;
regs[i].s_reg = INVALID_SREG;
+ size_t map_size = reginfo_map_.Size();
+ if (reg_number >= map_size) {
+ for (uint32_t i = 0; i < ((reg_number - map_size) + 1); i++) {
+ reginfo_map_.Insert(NULL);
+ }
+ }
+ reginfo_map_.Put(reg_number, &regs[i]);
}
}
@@ -62,10 +66,9 @@ void Mir2Lir::DumpRegPool(RegisterInfo* p, int num_regs) {
LOG(INFO) << "================================================";
for (int i = 0; i < num_regs; i++) {
LOG(INFO) << StringPrintf(
- "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d, ST:%x, EN:%x",
+ "R[%d]: T:%d, U:%d, P:%d, p:%d, LV:%d, D:%d, SR:%d",
p[i].reg, p[i].is_temp, p[i].in_use, p[i].pair, p[i].partner,
- p[i].live, p[i].dirty, p[i].s_reg, reinterpret_cast<uintptr_t>(p[i].def_start),
- reinterpret_cast<uintptr_t>(p[i].def_end));
+ p[i].live, p[i].dirty, p[i].s_reg);
}
LOG(INFO) << "================================================";
}
@@ -170,17 +173,12 @@ void Mir2Lir::RecordFpPromotion(int reg, int s_reg) {
promotion_map_[p_map_idx].FpReg = reg;
}
-/*
- * Reserve a callee-save fp single register. Try to fullfill request for
- * even/odd allocation, but go ahead and allocate anything if not
- * available. If nothing's available, return -1.
- */
-int Mir2Lir::AllocPreservedSingle(int s_reg, bool even) {
- int res = -1;
+// Reserve a callee-save fp single register.
+int Mir2Lir::AllocPreservedSingle(int s_reg) {
+ int res = -1; // Return code if none available.
RegisterInfo* FPRegs = reg_pool_->FPRegs;
for (int i = 0; i < reg_pool_->num_fp_regs; i++) {
- if (!FPRegs[i].is_temp && !FPRegs[i].in_use &&
- ((FPRegs[i].reg & 0x1) == 0) == even) {
+ if (!FPRegs[i].is_temp && !FPRegs[i].in_use) {
res = FPRegs[i].reg;
RecordFpPromotion(res, s_reg);
break;
@@ -246,26 +244,6 @@ int Mir2Lir::AllocPreservedDouble(int s_reg) {
return res;
}
-
-/*
- * Reserve a callee-save fp register. If this register can be used
- * as the first of a double, attempt to allocate an even pair of fp
- * single regs (but if can't still attempt to allocate a single, preferring
- * first to allocate an odd register.
- */
-int Mir2Lir::AllocPreservedFPReg(int s_reg, bool double_start) {
- int res = -1;
- if (double_start) {
- res = AllocPreservedDouble(s_reg);
- }
- if (res == -1) {
- res = AllocPreservedSingle(s_reg, false /* try odd # */);
- }
- if (res == -1)
- res = AllocPreservedSingle(s_reg, true /* try even # */);
- return res;
-}
-
int Mir2Lir::AllocTempBody(RegisterInfo* p, int num_regs, int* next_temp,
bool required) {
int next = *next_temp;
@@ -379,7 +357,7 @@ Mir2Lir::RegisterInfo* Mir2Lir::AllocLiveBody(RegisterInfo* p, int num_regs, int
if (s_reg == -1)
return NULL;
for (int i = 0; i < num_regs; i++) {
- if (p[i].live && (p[i].s_reg == s_reg)) {
+ if ((p[i].s_reg == s_reg) && p[i].live) {
if (p[i].is_temp)
p[i].in_use = true;
return &p[i];
@@ -412,47 +390,16 @@ Mir2Lir::RegisterInfo* Mir2Lir::AllocLive(int s_reg, int reg_class) {
}
void Mir2Lir::FreeTemp(int reg) {
- RegisterInfo* p = reg_pool_->core_regs;
- int num_regs = reg_pool_->num_core_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- if (p[i].is_temp) {
- p[i].in_use = false;
- }
- p[i].pair = false;
- return;
- }
- }
- p = reg_pool_->FPRegs;
- num_regs = reg_pool_->num_fp_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- if (p[i].is_temp) {
- p[i].in_use = false;
- }
- p[i].pair = false;
- return;
- }
+ RegisterInfo* p = GetRegInfo(reg);
+ if (p->is_temp) {
+ p->in_use = false;
}
- LOG(FATAL) << "Tried to free a non-existant temp: r" << reg;
+ p->pair = false;
}
Mir2Lir::RegisterInfo* Mir2Lir::IsLive(int reg) {
- RegisterInfo* p = reg_pool_->core_regs;
- int num_regs = reg_pool_->num_core_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- return p[i].live ? &p[i] : NULL;
- }
- }
- p = reg_pool_->FPRegs;
- num_regs = reg_pool_->num_fp_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- return p[i].live ? &p[i] : NULL;
- }
- }
- return NULL;
+ RegisterInfo* p = GetRegInfo(reg);
+ return p->live ? p : NULL;
}
Mir2Lir::RegisterInfo* Mir2Lir::IsTemp(int reg) {
@@ -476,27 +423,10 @@ bool Mir2Lir::IsDirty(int reg) {
* allocated. Use with caution.
*/
void Mir2Lir::LockTemp(int reg) {
- RegisterInfo* p = reg_pool_->core_regs;
- int num_regs = reg_pool_->num_core_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- DCHECK(p[i].is_temp);
- p[i].in_use = true;
- p[i].live = false;
- return;
- }
- }
- p = reg_pool_->FPRegs;
- num_regs = reg_pool_->num_fp_regs;
- for (int i = 0; i< num_regs; i++) {
- if (p[i].reg == reg) {
- DCHECK(p[i].is_temp);
- p[i].in_use = true;
- p[i].live = false;
- return;
- }
- }
- LOG(FATAL) << "Tried to lock a non-existant temp: r" << reg;
+ RegisterInfo* p = GetRegInfo(reg);
+ DCHECK(p->is_temp);
+ p->in_use = true;
+ p->live = false;
}
void Mir2Lir::ResetDef(int reg) {
@@ -599,11 +529,13 @@ void Mir2Lir::ResetDefTracking() {
}
void Mir2Lir::ClobberAllRegs() {
- for (int i = 0; i< reg_pool_->num_core_regs; i++) {
- ClobberBody(&reg_pool_->core_regs[i]);
- }
- for (int i = 0; i< reg_pool_->num_fp_regs; i++) {
- ClobberBody(&reg_pool_->FPRegs[i]);
+ GrowableArray<RegisterInfo*>::Iterator iter(&tempreg_info_);
+ for (RegisterInfo* info = iter.Next(); info != NULL; info = iter.Next()) {
+ info->live = false;
+ info->s_reg = INVALID_SREG;
+ info->def_start = NULL;
+ info->def_end = NULL;
+ info->pair = false;
}
}
@@ -659,11 +591,13 @@ void Mir2Lir::MarkLive(int reg, int s_reg) {
void Mir2Lir::MarkTemp(int reg) {
RegisterInfo* info = GetRegInfo(reg);
+ tempreg_info_.Insert(info);
info->is_temp = true;
}
void Mir2Lir::UnmarkTemp(int reg) {
RegisterInfo* info = GetRegInfo(reg);
+ tempreg_info_.Delete(info);
info->is_temp = false;
}
@@ -834,9 +768,9 @@ RegLocation Mir2Lir::UpdateRawLoc(RegLocation loc) {
RegLocation Mir2Lir::EvalLocWide(RegLocation loc, int reg_class, bool update) {
DCHECK(loc.wide);
- int new_regs;
- int low_reg;
- int high_reg;
+ int32_t new_regs;
+ int32_t low_reg;
+ int32_t high_reg;
loc = UpdateLocWide(loc);
@@ -912,18 +846,22 @@ RegLocation Mir2Lir::EvalLoc(RegLocation loc, int reg_class, bool update) {
}
/* USE SSA names to count references of base Dalvik v_regs. */
-void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts) {
+void Mir2Lir::CountRefs(RefCounts* core_counts, RefCounts* fp_counts, size_t num_regs) {
for (int i = 0; i < mir_graph_->GetNumSSARegs(); i++) {
RegLocation loc = mir_graph_->reg_location_[i];
RefCounts* counts = loc.fp ? fp_counts : core_counts;
int p_map_idx = SRegToPMap(loc.s_reg_low);
- // Don't count easily regenerated immediates
- if (loc.fp || !IsInexpensiveConstant(loc)) {
+ if (loc.fp) {
+ if (loc.wide) {
+ // Treat doubles as a unit, using upper half of fp_counts array.
+ counts[p_map_idx + num_regs].count += mir_graph_->GetUseCount(i);
+ i++;
+ } else {
+ counts[p_map_idx].count += mir_graph_->GetUseCount(i);
+ }
+ } else if (!IsInexpensiveConstant(loc)) {
counts[p_map_idx].count += mir_graph_->GetUseCount(i);
}
- if (loc.wide && loc.fp && !loc.high_word) {
- counts[p_map_idx].double_start = true;
- }
}
}
@@ -942,7 +880,11 @@ static int SortCounts(const void *val1, const void *val2) {
void Mir2Lir::DumpCounts(const RefCounts* arr, int size, const char* msg) {
LOG(INFO) << msg;
for (int i = 0; i < size; i++) {
- LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count;
+ if ((arr[i].s_reg & STARTING_DOUBLE_SREG) != 0) {
+ LOG(INFO) << "s_reg[D" << (arr[i].s_reg & ~STARTING_DOUBLE_SREG) << "]: " << arr[i].count;
+ } else {
+ LOG(INFO) << "s_reg[" << arr[i].s_reg << "]: " << arr[i].count;
+ }
}
}
@@ -965,7 +907,7 @@ void Mir2Lir::DoPromotion() {
* count based on original Dalvik register name. Count refs
* separately based on type in order to give allocation
* preference to fp doubles - which must be allocated sequential
- * physical single fp registers started with an even-numbered
+ * physical single fp registers starting with an even-numbered
* reg.
* TUNING: replace with linear scan once we have the ability
* to describe register live ranges for GC.
@@ -974,7 +916,7 @@ void Mir2Lir::DoPromotion() {
static_cast<RefCounts*>(arena_->Alloc(sizeof(RefCounts) * num_regs,
ArenaAllocator::kAllocRegAlloc));
RefCounts *FpRegs =
- static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * num_regs,
+ static_cast<RefCounts *>(arena_->Alloc(sizeof(RefCounts) * num_regs * 2,
ArenaAllocator::kAllocRegAlloc));
// Set ssa names for original Dalvik registers
for (int i = 0; i < dalvik_regs; i++) {
@@ -982,46 +924,49 @@ void Mir2Lir::DoPromotion() {
}
// Set ssa name for Method*
core_regs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg();
- FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy
+ FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy.
+ FpRegs[dalvik_regs + num_regs].s_reg = mir_graph_->GetMethodSReg(); // for consistency.
// Set ssa names for compiler_temps
for (int i = 1; i <= cu_->num_compiler_temps; i++) {
CompilerTemp* ct = mir_graph_->compiler_temps_.Get(i);
core_regs[dalvik_regs + i].s_reg = ct->s_reg;
FpRegs[dalvik_regs + i].s_reg = ct->s_reg;
+ FpRegs[num_regs + dalvik_regs + i].s_reg = ct->s_reg;
}
- // Sum use counts of SSA regs by original Dalvik vreg.
- CountRefs(core_regs, FpRegs);
-
- /*
- * Ideally, we'd allocate doubles starting with an even-numbered
- * register. Bias the counts to try to allocate any vreg that's
- * used as the start of a pair first.
- */
+ // Duplicate in upper half to represent possible fp double starting sregs.
for (int i = 0; i < num_regs; i++) {
- if (FpRegs[i].double_start) {
- FpRegs[i].count *= 2;
- }
+ FpRegs[num_regs + i].s_reg = FpRegs[i].s_reg | STARTING_DOUBLE_SREG;
}
+ // Sum use counts of SSA regs by original Dalvik vreg.
+ CountRefs(core_regs, FpRegs, num_regs);
+
+
// Sort the count arrays
qsort(core_regs, num_regs, sizeof(RefCounts), SortCounts);
- qsort(FpRegs, num_regs, sizeof(RefCounts), SortCounts);
+ qsort(FpRegs, num_regs * 2, sizeof(RefCounts), SortCounts);
if (cu_->verbose) {
DumpCounts(core_regs, num_regs, "Core regs after sort");
- DumpCounts(FpRegs, num_regs, "Fp regs after sort");
+ DumpCounts(FpRegs, num_regs * 2, "Fp regs after sort");
}
if (!(cu_->disable_opt & (1 << kPromoteRegs))) {
// Promote FpRegs
- for (int i = 0; (i < num_regs) && (FpRegs[i].count >= promotion_threshold); i++) {
- int p_map_idx = SRegToPMap(FpRegs[i].s_reg);
- if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) {
- int reg = AllocPreservedFPReg(FpRegs[i].s_reg,
- FpRegs[i].double_start);
+ for (int i = 0; (i < (num_regs * 2)) && (FpRegs[i].count >= promotion_threshold); i++) {
+ int p_map_idx = SRegToPMap(FpRegs[i].s_reg & ~STARTING_DOUBLE_SREG);
+ if ((FpRegs[i].s_reg & STARTING_DOUBLE_SREG) != 0) {
+ if ((promotion_map_[p_map_idx].fp_location != kLocPhysReg) &&
+ (promotion_map_[p_map_idx + 1].fp_location != kLocPhysReg)) {
+ int low_sreg = FpRegs[i].s_reg & ~STARTING_DOUBLE_SREG;
+ // Ignore result - if can't alloc double may still be able to alloc singles.
+ AllocPreservedDouble(low_sreg);
+ }
+ } else if (promotion_map_[p_map_idx].fp_location != kLocPhysReg) {
+ int reg = AllocPreservedSingle(FpRegs[i].s_reg);
if (reg < 0) {
- break; // No more left
+ break; // No more left.
}
}
}
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index e8834320a9..2047f30765 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -246,6 +246,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0,
UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"),
#undef UNARY_ENCODING_MAP
+ { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0, { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" },
+
#define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \
{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \
{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RM", "!0r,[!1r+!2d]" }, \
@@ -362,6 +364,7 @@ static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacemen
}
int X86Mir2Lir::GetInsnSize(LIR* lir) {
+ DCHECK(!IsPseudoLirOp(lir->opcode));
const X86EncodingMap* entry = &X86Mir2Lir::EncodingMap[lir->opcode];
switch (entry->kind) {
case kData:
@@ -370,6 +373,8 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) {
return lir->operands[0]; // length of nop is sole operand
case kNullary:
return 1; // 1 byte of opcode
+ case kRegOpcode: // lir operands - 0: reg
+ return ComputeSize(entry, 0, 0, false) - 1; // substract 1 for modrm
case kReg: // lir operands - 0: reg
return ComputeSize(entry, 0, 0, false);
case kMem: // lir operands - 0: base, 1: disp
@@ -513,6 +518,33 @@ void X86Mir2Lir::EmitDisp(int base, int disp) {
}
}
+void X86Mir2Lir::EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg) {
+ if (entry->skeleton.prefix1 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix1);
+ if (entry->skeleton.prefix2 != 0) {
+ code_buffer_.push_back(entry->skeleton.prefix2);
+ }
+ } else {
+ DCHECK_EQ(0, entry->skeleton.prefix2);
+ }
+ code_buffer_.push_back(entry->skeleton.opcode);
+ if (entry->skeleton.opcode == 0x0F) {
+ code_buffer_.push_back(entry->skeleton.extra_opcode1);
+ // There's no 3-byte instruction with +rd
+ DCHECK_NE(0x38, entry->skeleton.extra_opcode1);
+ DCHECK_NE(0x3A, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ } else {
+ DCHECK_EQ(0, entry->skeleton.extra_opcode1);
+ DCHECK_EQ(0, entry->skeleton.extra_opcode2);
+ }
+ DCHECK(!X86_FPREG(reg));
+ DCHECK_LT(reg, 8);
+ code_buffer_.back() += reg;
+ DCHECK_EQ(0, entry->skeleton.ax_opcode);
+ DCHECK_EQ(0, entry->skeleton.immediate_bytes);
+}
+
void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) {
if (entry->skeleton.prefix1 != 0) {
code_buffer_.push_back(entry->skeleton.prefix1);
@@ -525,7 +557,7 @@ void X86Mir2Lir::EmitOpReg(const X86EncodingMap* entry, uint8_t reg) {
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -582,7 +614,7 @@ void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry,
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -595,7 +627,9 @@ void X86Mir2Lir::EmitMemReg(const X86EncodingMap* entry,
reg = reg & X86_FP_REG_MASK;
}
if (reg >= 4) {
- DCHECK(strchr(entry->name, '8') == NULL) << entry->name << " " << static_cast<int>(reg)
+ DCHECK(strchr(entry->name, '8') == NULL ||
+ entry->opcode == kX86Movzx8RM || entry->opcode == kX86Movsx8RM)
+ << entry->name << " " << static_cast<int>(reg)
<< " in " << PrettyMethod(cu_->method_idx, *cu_->dex_file);
}
DCHECK_LT(reg, 8);
@@ -631,7 +665,7 @@ void X86Mir2Lir::EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -672,7 +706,7 @@ void X86Mir2Lir::EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int dis
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -712,7 +746,7 @@ void X86Mir2Lir::EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t r
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -749,7 +783,7 @@ void X86Mir2Lir::EmitRegRegImm(const X86EncodingMap* entry,
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -808,7 +842,7 @@ void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) {
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -858,7 +892,7 @@ void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) {
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -923,7 +957,7 @@ void X86Mir2Lir::EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int i
}
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -1037,7 +1071,7 @@ void X86Mir2Lir::EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -1066,7 +1100,7 @@ void X86Mir2Lir::EmitCallThread(const X86EncodingMap* entry, int disp) {
code_buffer_.push_back(entry->skeleton.opcode);
if (entry->skeleton.opcode == 0x0F) {
code_buffer_.push_back(entry->skeleton.extra_opcode1);
- if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode2 == 0x3A) {
+ if (entry->skeleton.extra_opcode1 == 0x38 || entry->skeleton.extra_opcode1 == 0x3A) {
code_buffer_.push_back(entry->skeleton.extra_opcode2);
} else {
DCHECK_EQ(0, entry->skeleton.extra_opcode2);
@@ -1089,11 +1123,13 @@ void X86Mir2Lir::EmitPcRel(const X86EncodingMap* entry, uint8_t reg,
int base_or_table, uint8_t index, int scale, int table_or_disp) {
int disp;
if (entry->opcode == kX86PcRelLoadRA) {
- Mir2Lir::SwitchTable *tab_rec = reinterpret_cast<Mir2Lir::SwitchTable*>(table_or_disp);
+ Mir2Lir::EmbeddedData *tab_rec =
+ reinterpret_cast<Mir2Lir::EmbeddedData*>(UnwrapPointer(table_or_disp));
disp = tab_rec->offset;
} else {
DCHECK(entry->opcode == kX86PcRelAdr);
- Mir2Lir::FillArrayData *tab_rec = reinterpret_cast<Mir2Lir::FillArrayData*>(base_or_table);
+ Mir2Lir::EmbeddedData *tab_rec =
+ reinterpret_cast<Mir2Lir::EmbeddedData*>(UnwrapPointer(base_or_table));
disp = tab_rec->offset;
}
if (entry->skeleton.prefix1 != 0) {
@@ -1160,13 +1196,13 @@ void X86Mir2Lir::EmitUnimplemented(const X86EncodingMap* entry, LIR* lir) {
* instruction. In those cases we will try to substitute a new code
* sequence or request that the trace be shortened and retried.
*/
-AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
+AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) {
LIR *lir;
AssemblerStatus res = kSuccess; // Assume success
const bool kVerbosePcFixup = false;
for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
- if (lir->opcode < 0) {
+ if (IsPseudoLirOp(lir->opcode)) {
continue;
}
@@ -1174,19 +1210,19 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
continue;
}
- if (lir->flags.pcRelFixup) {
+ if (lir->flags.fixup != kFixupNone) {
switch (lir->opcode) {
case kX86Jcc8: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
int delta = 0;
- uintptr_t pc;
+ CodeOffset pc;
if (IS_SIMM8(lir->operands[0])) {
pc = lir->offset + 2 /* opcode + rel8 */;
} else {
pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
}
- uintptr_t target = target_lir->offset;
+ CodeOffset target = target_lir->offset;
delta = target - pc;
if (IS_SIMM8(delta) != IS_SIMM8(lir->operands[0])) {
if (kVerbosePcFixup) {
@@ -1210,8 +1246,8 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
case kX86Jcc32: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
- uintptr_t pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 6 /* 2 byte opcode + rel32 */;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
if (kVerbosePcFixup) {
LOG(INFO) << "Source:";
@@ -1227,17 +1263,17 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
int delta = 0;
- uintptr_t pc;
+ CodeOffset pc;
if (IS_SIMM8(lir->operands[0])) {
pc = lir->offset + 2 /* opcode + rel8 */;
} else {
pc = lir->offset + 5 /* opcode + rel32 */;
}
- uintptr_t target = target_lir->offset;
+ CodeOffset target = target_lir->offset;
delta = target - pc;
if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && delta == 0) {
// Useless branch
- lir->flags.is_nop = true;
+ NopLIR(lir);
if (kVerbosePcFixup) {
LOG(INFO) << "Retry for useless branch at " << lir->offset;
}
@@ -1256,8 +1292,8 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
case kX86Jmp32: {
LIR *target_lir = lir->target;
DCHECK(target_lir != NULL);
- uintptr_t pc = lir->offset + 5 /* opcode + rel32 */;
- uintptr_t target = target_lir->offset;
+ CodeOffset pc = lir->offset + 5 /* opcode + rel32 */;
+ CodeOffset target = target_lir->offset;
int delta = target - pc;
lir->operands[0] = delta;
break;
@@ -1298,6 +1334,9 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
DCHECK_EQ(0, entry->skeleton.ax_opcode);
DCHECK_EQ(0, entry->skeleton.immediate_bytes);
break;
+ case kRegOpcode: // lir operands - 0: reg
+ EmitOpRegOpcode(entry, lir->operands[0]);
+ break;
case kReg: // lir operands - 0: reg
EmitOpReg(entry, lir->operands[0]);
break;
@@ -1385,4 +1424,101 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(uintptr_t start_addr) {
return res;
}
+// LIR offset assignment.
+// TODO: consolidate w/ Arm assembly mechanism.
+int X86Mir2Lir::AssignInsnOffsets() {
+ LIR* lir;
+ int offset = 0;
+
+ for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) {
+ lir->offset = offset;
+ if (LIKELY(!IsPseudoLirOp(lir->opcode))) {
+ if (!lir->flags.is_nop) {
+ offset += lir->flags.size;
+ }
+ } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) {
+ if (offset & 0x2) {
+ offset += 2;
+ lir->operands[0] = 1;
+ } else {
+ lir->operands[0] = 0;
+ }
+ }
+ /* Pseudo opcodes don't consume space */
+ }
+ return offset;
+}
+
+/*
+ * Walk the compilation unit and assign offsets to instructions
+ * and literals and compute the total size of the compiled unit.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void X86Mir2Lir::AssignOffsets() {
+ int offset = AssignInsnOffsets();
+
+ /* Const values have to be word aligned */
+ offset = (offset + 3) & ~3;
+
+ /* Set up offsets for literals */
+ data_offset_ = offset;
+
+ offset = AssignLiteralOffset(offset);
+
+ offset = AssignSwitchTablesOffset(offset);
+
+ offset = AssignFillArrayDataOffset(offset);
+
+ total_size_ = offset;
+}
+
+/*
+ * Go over each instruction in the list and calculate the offset from the top
+ * before sending them off to the assembler. If out-of-range branch distance is
+ * seen rearrange the instructions a bit to correct it.
+ * TODO: consolidate w/ Arm assembly mechanism.
+ */
+void X86Mir2Lir::AssembleLIR() {
+ cu_->NewTimingSplit("Assemble");
+ AssignOffsets();
+ int assembler_retries = 0;
+ /*
+ * Assemble here. Note that we generate code with optimistic assumptions
+ * and if found now to work, we'll have to redo the sequence and retry.
+ */
+
+ while (true) {
+ AssemblerStatus res = AssembleInstructions(0);
+ if (res == kSuccess) {
+ break;
+ } else {
+ assembler_retries++;
+ if (assembler_retries > MAX_ASSEMBLER_RETRIES) {
+ CodegenDump();
+ LOG(FATAL) << "Assembler error - too many retries";
+ }
+ // Redo offsets and try again
+ AssignOffsets();
+ code_buffer_.clear();
+ }
+ }
+
+ cu_->NewTimingSplit("LiteralData");
+ // Install literals
+ InstallLiteralPools();
+
+ // Install switch tables
+ InstallSwitchTables();
+
+ // Install fill array data
+ InstallFillArrayData();
+
+ // Create the mapping table and native offset to reference map.
+ cu_->NewTimingSplit("PcMappingTable");
+ CreateMappingTables();
+
+ cu_->NewTimingSplit("GcMap");
+ CreateNativeGcMap();
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 2be2aa9a0e..17924b0f08 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -31,15 +31,15 @@ void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
* The sparse table in the literal pool is an array of <key,displacement>
* pairs.
*/
-void X86Mir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
+void X86Mir2Lir::GenSparseSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpSparseSwitchTable(table);
}
int entries = table[1];
- const int* keys = reinterpret_cast<const int*>(&table[2]);
- const int* targets = &keys[entries];
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&table[2]);
+ const int32_t* targets = &keys[entries];
rl_src = LoadValue(rl_src, kCoreReg);
for (int i = 0; i < entries; i++) {
int key = keys[i];
@@ -66,15 +66,15 @@ void X86Mir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
* jmp r_start_of_method
* done:
*/
-void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
+void X86Mir2Lir::GenPackedSwitch(MIR* mir, DexOffset table_offset,
RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
if (cu_->verbose) {
DumpPackedSwitchTable(table);
}
// Add the table to the list - we'll process it later
- SwitchTable *tab_rec =
- static_cast<SwitchTable *>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
+ SwitchTable* tab_rec =
+ static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
int size = table[1];
@@ -103,8 +103,7 @@ void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
// Load the displacement from the switch table
int disp_reg = AllocTemp();
- NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2,
- reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR5(kX86PcRelLoadRA, disp_reg, start_of_method_reg, keyReg, 2, WrapPointer(tab_rec));
// Add displacement to start of method
OpRegReg(kOpAdd, start_of_method_reg, disp_reg);
// ..and go!
@@ -126,10 +125,10 @@ void X86Mir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
*
* Total size is 4+(width * size + 1)/2 16-bit code units.
*/
-void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
+void X86Mir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
// Add the table to the list - we'll process it later
- FillArrayData *tab_rec =
+ FillArrayData* tab_rec =
static_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData), ArenaAllocator::kAllocData));
tab_rec->table = table;
tab_rec->vaddr = current_dalvik_offset_;
@@ -144,49 +143,12 @@ void X86Mir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
LoadValueDirectFixed(rl_src, rX86_ARG0);
// Materialize a pointer to the fill data image
NewLIR1(kX86StartOfMethod, rX86_ARG2);
- NewLIR2(kX86PcRelAdr, rX86_ARG1, reinterpret_cast<uintptr_t>(tab_rec));
+ NewLIR2(kX86PcRelAdr, rX86_ARG1, WrapPointer(tab_rec));
NewLIR2(kX86Add32RR, rX86_ARG1, rX86_ARG2);
CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayData), rX86_ARG0,
rX86_ARG1, true);
}
-void X86Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rCX); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rCX, opt_flags);
- // If lock is unheld, try to grab it quickly with compare and exchange
- // TODO: copy and clear hash state?
- NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value());
- NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT);
- NewLIR2(kX86Xor32RR, rAX, rAX);
- NewLIR3(kX86LockCmpxchgMR, rCX, mirror::Object::MonitorOffset().Int32Value(), rDX);
- LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondEq);
- // If lock is held, go the expensive route - artLockObjectFromCode(self, obj);
- CallRuntimeHelperReg(QUICK_ENTRYPOINT_OFFSET(pLockObject), rCX, true);
- branch->target = NewLIR0(kPseudoTargetLabel);
-}
-
-void X86Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
- FlushAllRegs();
- LoadValueDirectFixed(rl_src, rAX); // Get obj
- LockCallTemps(); // Prepare for explicit register usage
- GenNullCheck(rl_src.s_reg_low, rAX, opt_flags);
- // If lock is held by the current thread, clear it to quickly release it
- // TODO: clear hash state?
- NewLIR2(kX86Mov32RT, rDX, Thread::ThinLockIdOffset().Int32Value());
- NewLIR2(kX86Sal32RI, rDX, LW_LOCK_OWNER_SHIFT);
- NewLIR3(kX86Mov32RM, rCX, rAX, mirror::Object::MonitorOffset().Int32Value());
- OpRegReg(kOpSub, rCX, rDX);
- LIR* branch = NewLIR2(kX86Jcc8, 0, kX86CondNe);
- NewLIR3(kX86Mov32MR, rAX, mirror::Object::MonitorOffset().Int32Value(), rCX);
- LIR* branch2 = NewLIR1(kX86Jmp8, 0);
- branch->target = NewLIR0(kPseudoTargetLabel);
- // Otherwise, go the expensive route - UnlockObjectFromCode(obj);
- CallRuntimeHelperReg(QUICK_ENTRYPOINT_OFFSET(pUnlockObject), rAX, true);
- branch2->target = NewLIR0(kPseudoTargetLabel);
-}
-
void X86Mir2Lir::GenMoveException(RegLocation rl_dest) {
int ex_offset = Thread::ExceptionOffset().Int32Value();
RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 478654d0b4..1d6509eea5 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -52,7 +52,6 @@ class X86Mir2Lir : public Mir2Lir {
int AllocTypedTempPair(bool fp_hint, int reg_class);
int S2d(int low_reg, int high_reg);
int TargetReg(SpecialTargetRegister reg);
- RegisterInfo* GetRegInfo(int reg);
RegLocation GetReturnAlt();
RegLocation GetReturnWideAlt();
RegLocation LocCReturn();
@@ -72,9 +71,12 @@ class X86Mir2Lir : public Mir2Lir {
void CompilerInitializeRegAlloc();
// Required for target - miscellaneous.
- AssemblerStatus AssembleInstructions(uintptr_t start_addr);
+ void AssembleLIR();
+ int AssignInsnOffsets();
+ void AssignOffsets();
+ AssemblerStatus AssembleInstructions(CodeOffset start_addr);
void DumpResourceMask(LIR* lir, uint64_t mask, const char* prefix);
- void SetupTargetResourceMasks(LIR* lir);
+ void SetupTargetResourceMasks(LIR* lir, uint64_t flags);
const char* GetTargetInstFmt(int opcode);
const char* GetTargetInstName(int opcode);
std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr);
@@ -86,14 +88,12 @@ class X86Mir2Lir : public Mir2Lir {
// Required for target - Dalvik-level generators.
void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_src2);
- void GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
RegLocation rl_index, RegLocation rl_dest, int scale);
void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale);
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark);
void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest,
- RegLocation rl_src1, RegLocation rl_shift);
+ RegLocation rl_src1, RegLocation rl_shift);
void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenAndLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -107,6 +107,8 @@ class X86Mir2Lir : public Mir2Lir {
bool GenInlinedCas32(CallInfo* info, bool need_write_barrier);
bool GenInlinedMinMaxInt(CallInfo* info, bool is_min);
bool GenInlinedSqrt(CallInfo* info);
+ bool GenInlinedPeek(CallInfo* info, OpSize size);
+ bool GenInlinedPoke(CallInfo* info, OpSize size);
void GenNegLong(RegLocation rl_dest, RegLocation rl_src);
void GenOrLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -119,20 +121,18 @@ class X86Mir2Lir : public Mir2Lir {
void GenDivZeroCheck(int reg_lo, int reg_hi);
void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method);
void GenExitSequence();
- void GenFillArrayData(uint32_t table_offset, RegLocation rl_src);
+ void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
void GenMemBarrier(MemBarrierKind barrier_kind);
- void GenMonitorEnter(int opt_flags, RegLocation rl_src);
- void GenMonitorExit(int opt_flags, RegLocation rl_src);
void GenMoveException(RegLocation rl_dest);
void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result,
int lit, int first_bit, int second_bit);
void GenNegDouble(RegLocation rl_dest, RegLocation rl_src);
void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
- void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
- void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
+ void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
+ void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
// Single operation generators.
@@ -172,6 +172,7 @@ class X86Mir2Lir : public Mir2Lir {
private:
void EmitDisp(int base, int disp);
+ void EmitOpRegOpcode(const X86EncodingMap* entry, uint8_t reg);
void EmitOpReg(const X86EncodingMap* entry, uint8_t reg);
void EmitOpMem(const X86EncodingMap* entry, uint8_t base, int disp);
void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index f736b5e28f..c9d6bfc8cc 100644
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -284,8 +284,8 @@ void X86Mir2Lir::GenCmpFP(Instruction::Code code, RegLocation rl_dest,
void X86Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias,
bool is_double) {
- LIR* taken = &block_label_list_[bb->taken->id];
- LIR* not_taken = &block_label_list_[bb->fall_through->id];
+ LIR* taken = &block_label_list_[bb->taken];
+ LIR* not_taken = &block_label_list_[bb->fall_through];
LIR* branch = NULL;
RegLocation rl_src1;
RegLocation rl_src2;
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 14be7dde90..499547bb37 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -166,7 +166,7 @@ void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
}
void X86Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
- LIR* taken = &block_label_list_[bb->taken->id];
+ LIR* taken = &block_label_list_[bb->taken];
RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
FlushAllRegs();
@@ -236,6 +236,43 @@ bool X86Mir2Lir::GenInlinedMinMaxInt(CallInfo* info, bool is_min) {
return true;
}
+bool X86Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
+ if (size == kLong) {
+ // Unaligned access is allowed on x86.
+ LoadBaseDispWide(rl_address.low_reg, 0, rl_result.low_reg, rl_result.high_reg, INVALID_SREG);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned access is allowed on x86.
+ LoadBaseDisp(rl_address.low_reg, 0, rl_result.low_reg, size, INVALID_SREG);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
+bool X86Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) {
+ RegLocation rl_src_address = info->args[0]; // long address
+ rl_src_address.wide = 0; // ignore high half in info->args[1]
+ RegLocation rl_src_value = info->args[2]; // [size] value
+ RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+ if (size == kLong) {
+ // Unaligned access is allowed on x86.
+ RegLocation rl_value = LoadValueWide(rl_src_value, kCoreReg);
+ StoreBaseDispWide(rl_address.low_reg, 0, rl_value.low_reg, rl_value.high_reg);
+ } else {
+ DCHECK(size == kSignedByte || size == kSignedHalf || size == kWord);
+ // Unaligned access is allowed on x86.
+ RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
+ StoreBaseDisp(rl_address.low_reg, 0, rl_value.low_reg, size);
+ }
+ return true;
+}
+
void X86Mir2Lir::OpLea(int rBase, int reg1, int reg2, int scale, int offset) {
NewLIR5(kX86Lea32RA, rBase, reg1, reg2, scale, offset);
}
@@ -419,7 +456,7 @@ void X86Mir2Lir::OpRegThreadMem(OpKind op, int r_dest, ThreadOffset thread_offse
* Generate array load
*/
void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_dest, int scale) {
+ RegLocation rl_index, RegLocation rl_dest, int scale) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -466,7 +503,7 @@ void X86Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
*
*/
void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
+ RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark) {
RegisterClass reg_class = oat_reg_class_by_size(size);
int len_offset = mirror::Array::LengthOffset().Int32Value();
int data_offset;
@@ -502,59 +539,10 @@ void X86Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
StoreBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_src.low_reg,
rl_src.high_reg, size, INVALID_SREG);
}
-}
-
-/*
- * Generate array store
- *
- */
-void X86Mir2Lir::GenArrayObjPut(int opt_flags, RegLocation rl_array,
- RegLocation rl_index, RegLocation rl_src, int scale) {
- int len_offset = mirror::Array::LengthOffset().Int32Value();
- int data_offset = mirror::Array::DataOffset(sizeof(mirror::Object*)).Int32Value();
-
- FlushAllRegs(); // Use explicit registers
- LockCallTemps();
-
- int r_value = TargetReg(kArg0); // Register holding value
- int r_array_class = TargetReg(kArg1); // Register holding array's Class
- int r_array = TargetReg(kArg2); // Register holding array
- int r_index = TargetReg(kArg3); // Register holding index into array
-
- LoadValueDirectFixed(rl_array, r_array); // Grab array
- LoadValueDirectFixed(rl_src, r_value); // Grab value
- LoadValueDirectFixed(rl_index, r_index); // Grab index
-
- GenNullCheck(rl_array.s_reg_low, r_array, opt_flags); // NPE?
-
- // Store of null?
- LIR* null_value_check = OpCmpImmBranch(kCondEq, r_value, 0, NULL);
-
- // Get the array's class.
- LoadWordDisp(r_array, mirror::Object::ClassOffset().Int32Value(), r_array_class);
- CallRuntimeHelperRegReg(QUICK_ENTRYPOINT_OFFSET(pCanPutArrayElement), r_value,
- r_array_class, true);
- // Redo LoadValues in case they didn't survive the call.
- LoadValueDirectFixed(rl_array, r_array); // Reload array
- LoadValueDirectFixed(rl_index, r_index); // Reload index
- LoadValueDirectFixed(rl_src, r_value); // Reload value
- r_array_class = INVALID_REG;
-
- // Branch here if value to be stored == null
- LIR* target = NewLIR0(kPseudoTargetLabel);
- null_value_check->target = target;
-
- // make an extra temp available for card mark below
- FreeTemp(TargetReg(kArg1));
- if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
- /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
- GenRegMemCheck(kCondUge, r_index, r_array, len_offset, kThrowArrayBounds);
- }
- StoreBaseIndexedDisp(r_array, r_index, scale,
- data_offset, r_value, INVALID_REG, kWord, INVALID_SREG);
- FreeTemp(r_index);
- if (!mir_graph_->IsConstantNullRef(rl_src)) {
- MarkGCCard(r_value, r_array);
+ if (card_mark) {
+ // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
+ FreeTemp(rl_index.low_reg);
+ MarkGCCard(rl_src.low_reg, rl_array.low_reg);
}
}
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 26accab360..878fa769b6 100644
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -85,6 +85,8 @@ int X86Mir2Lir::TargetReg(SpecialTargetRegister reg) {
case kRet0: res = rX86_RET0; break;
case kRet1: res = rX86_RET1; break;
case kInvokeTgt: res = rX86_INVOKE_TGT; break;
+ case kHiddenArg: res = rAX; break;
+ case kHiddenFpArg: res = fr0; break;
case kCount: res = rX86_COUNT; break;
}
return res;
@@ -132,37 +134,36 @@ uint64_t X86Mir2Lir::GetPCUseDefEncoding() {
return 0ULL;
}
-void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir) {
+void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) {
DCHECK_EQ(cu_->instruction_set, kX86);
+ DCHECK(!lir->flags.use_def_invalid);
// X86-specific resource map setup here.
- uint64_t flags = X86Mir2Lir::EncodingMap[lir->opcode].flags;
-
if (flags & REG_USE_SP) {
- lir->use_mask |= ENCODE_X86_REG_SP;
+ lir->u.m.use_mask |= ENCODE_X86_REG_SP;
}
if (flags & REG_DEF_SP) {
- lir->def_mask |= ENCODE_X86_REG_SP;
+ lir->u.m.def_mask |= ENCODE_X86_REG_SP;
}
if (flags & REG_DEFA) {
- SetupRegMask(&lir->def_mask, rAX);
+ SetupRegMask(&lir->u.m.def_mask, rAX);
}
if (flags & REG_DEFD) {
- SetupRegMask(&lir->def_mask, rDX);
+ SetupRegMask(&lir->u.m.def_mask, rDX);
}
if (flags & REG_USEA) {
- SetupRegMask(&lir->use_mask, rAX);
+ SetupRegMask(&lir->u.m.use_mask, rAX);
}
if (flags & REG_USEC) {
- SetupRegMask(&lir->use_mask, rCX);
+ SetupRegMask(&lir->u.m.use_mask, rCX);
}
if (flags & REG_USED) {
- SetupRegMask(&lir->use_mask, rDX);
+ SetupRegMask(&lir->u.m.use_mask, rDX);
}
}
@@ -224,7 +225,7 @@ std::string X86Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char
buf += StringPrintf("%d", operand);
break;
case 'p': {
- SwitchTable *tab_rec = reinterpret_cast<SwitchTable*>(operand);
+ EmbeddedData *tab_rec = reinterpret_cast<EmbeddedData*>(UnwrapPointer(operand));
buf += StringPrintf("0x%08x", tab_rec->offset);
break;
}
@@ -239,7 +240,7 @@ std::string X86Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char
break;
case 't':
buf += StringPrintf("0x%08x (L%p)",
- reinterpret_cast<uint32_t>(base_addr)
+ reinterpret_cast<uintptr_t>(base_addr)
+ lir->offset + operand, lir->target);
break;
default:
@@ -275,8 +276,8 @@ void X86Mir2Lir::DumpResourceMask(LIR *x86LIR, uint64_t mask, const char *prefix
}
/* Memory bits */
if (x86LIR && (mask & ENCODE_DALVIK_REG)) {
- sprintf(buf + strlen(buf), "dr%d%s", x86LIR->alias_info & 0xffff,
- (x86LIR->alias_info & 0x80000000) ? "(+1)" : "");
+ sprintf(buf + strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(x86LIR->flags.alias_info),
+ (DECODE_ALIAS_INFO_WIDE(x86LIR->flags.alias_info)) ? "(+1)" : "");
}
if (mask & ENCODE_LITERAL) {
strcat(buf, "lit ");
@@ -375,11 +376,6 @@ RegLocation X86Mir2Lir::GetReturnAlt() {
return res;
}
-X86Mir2Lir::RegisterInfo* X86Mir2Lir::GetRegInfo(int reg) {
- return X86_FPREG(reg) ? &reg_pool_->FPRegs[reg & X86_FP_REG_MASK]
- : &reg_pool_->core_regs[reg];
-}
-
/* To be used when explicitly managing register use */
void X86Mir2Lir::LockCallTemps() {
LockTemp(rX86_ARG0);
@@ -530,14 +526,17 @@ int X86Mir2Lir::LoadHelper(ThreadOffset offset) {
}
uint64_t X86Mir2Lir::GetTargetInstFlags(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return X86Mir2Lir::EncodingMap[opcode].flags;
}
const char* X86Mir2Lir::GetTargetInstName(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return X86Mir2Lir::EncodingMap[opcode].name;
}
const char* X86Mir2Lir::GetTargetInstFmt(int opcode) {
+ DCHECK(!IsPseudoLirOp(opcode));
return X86Mir2Lir::EncodingMap[opcode].fmt;
}
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index c519bfec44..6ec7ebb91a 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -117,6 +117,7 @@ LIR* X86Mir2Lir::OpReg(OpKind op, int r_dest_src) {
switch (op) {
case kOpNeg: opcode = kX86Neg32R; break;
case kOpNot: opcode = kX86Not32R; break;
+ case kOpRev: opcode = kX86Bswap32R; break;
case kOpBlx: opcode = kX86CallR; break;
default:
LOG(FATAL) << "Bad case in OpReg " << op;
@@ -161,6 +162,13 @@ LIR* X86Mir2Lir::OpRegReg(OpKind op, int r_dest_src1, int r_src2) {
case kOpNeg:
OpRegCopy(r_dest_src1, r_src2);
return OpReg(kOpNeg, r_dest_src1);
+ case kOpRev:
+ OpRegCopy(r_dest_src1, r_src2);
+ return OpReg(kOpRev, r_dest_src1);
+ case kOpRevsh:
+ OpRegCopy(r_dest_src1, r_src2);
+ OpReg(kOpRev, r_dest_src1);
+ return OpRegImm(kOpAsr, r_dest_src1, 16);
// X86 binary opcodes
case kOpSub: opcode = kX86Sub32RR; break;
case kOpSbc: opcode = kX86Sbb32RR; break;
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 643a3d5b8f..3518131cfe 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -243,7 +243,7 @@ enum X86OpCode {
// - lir operands - 0: base, 1: disp, 2: immediate
// AI - Array Immediate - opcode [base + index * scale + disp], #immediate
// - lir operands - 0: base, 1: index, 2: scale, 3: disp 4: immediate
- // TI - Thread Register - opcode fs:[disp], imm - where fs: is equal to Thread::Current()
+ // TI - Thread Immediate - opcode fs:[disp], imm - where fs: is equal to Thread::Current()
// - lir operands - 0: disp, 1: imm
#define BinaryOpCode(opcode) \
opcode ## 8MR, opcode ## 8AR, opcode ## 8TR, \
@@ -313,6 +313,7 @@ enum X86OpCode {
UnaryOpcode(kX86Imul, DaR, DaM, DaA),
UnaryOpcode(kX86Divmod, DaR, DaM, DaA),
UnaryOpcode(kX86Idivmod, DaR, DaM, DaA),
+ kX86Bswap32R,
#undef UnaryOpcode
#define Binary0fOpCode(opcode) \
opcode ## RR, opcode ## RM, opcode ## RA
@@ -381,6 +382,7 @@ enum X86EncodingKind {
kData, // Special case for raw data.
kNop, // Special case for variable length nop.
kNullary, // Opcode that takes no arguments.
+ kRegOpcode, // Shorter form of R instruction kind (opcode+rd)
kReg, kMem, kArray, // R, M and A instruction kinds.
kMemReg, kArrayReg, kThreadReg, // MR, AR and TR instruction kinds.
kRegReg, kRegMem, kRegArray, kRegThread, // RR, RM, RA and RT instruction kinds.
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index cd1602f674..0d8bd07f40 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -22,7 +22,7 @@
namespace art {
void MIRGraph::ClearAllVisitedFlags() {
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
bb->visited = false;
}
@@ -38,18 +38,18 @@ BasicBlock* MIRGraph::NeedsVisit(BasicBlock* bb) {
}
BasicBlock* MIRGraph::NextUnvisitedSuccessor(BasicBlock* bb) {
- BasicBlock* res = NeedsVisit(bb->fall_through);
+ BasicBlock* res = NeedsVisit(GetBasicBlock(bb->fall_through));
if (res == NULL) {
- res = NeedsVisit(bb->taken);
+ res = NeedsVisit(GetBasicBlock(bb->taken));
if (res == NULL) {
- if (bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ if (bb->successor_block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
while (true) {
SuccessorBlockInfo *sbi = iterator.Next();
if (sbi == NULL) {
break;
}
- res = NeedsVisit(sbi->block);
+ res = NeedsVisit(GetBasicBlock(sbi->block));
if (res != NULL) {
break;
}
@@ -63,7 +63,9 @@ BasicBlock* MIRGraph::NextUnvisitedSuccessor(BasicBlock* bb) {
void MIRGraph::MarkPreOrder(BasicBlock* block) {
block->visited = true;
/* Enqueue the pre_order block id */
- dfs_order_->Insert(block->id);
+ if (block->id != NullBasicBlockId) {
+ dfs_order_->Insert(block->id);
+ }
}
void MIRGraph::RecordDFSOrders(BasicBlock* block) {
@@ -79,7 +81,9 @@ void MIRGraph::RecordDFSOrders(BasicBlock* block) {
continue;
}
curr->dfs_id = dfs_post_order_->Size();
- dfs_post_order_->Insert(curr->id);
+ if (curr->id != NullBasicBlockId) {
+ dfs_post_order_->Insert(curr->id);
+ }
succ.pop_back();
}
}
@@ -88,7 +92,8 @@ void MIRGraph::RecordDFSOrders(BasicBlock* block) {
void MIRGraph::ComputeDFSOrders() {
/* Initialize or reset the DFS pre_order list */
if (dfs_order_ == NULL) {
- dfs_order_ = new (arena_) GrowableArray<int>(arena_, GetNumBlocks(), kGrowableArrayDfsOrder);
+ dfs_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
+ kGrowableArrayDfsOrder);
} else {
/* Just reset the used length on the counter */
dfs_order_->Reset();
@@ -96,7 +101,8 @@ void MIRGraph::ComputeDFSOrders() {
/* Initialize or reset the DFS post_order list */
if (dfs_post_order_ == NULL) {
- dfs_post_order_ = new (arena_) GrowableArray<int>(arena_, GetNumBlocks(), kGrowableArrayDfsPostOrder);
+ dfs_post_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks(),
+ kGrowableArrayDfsPostOrder);
} else {
/* Just reset the used length on the counter */
dfs_post_order_->Reset();
@@ -145,11 +151,11 @@ void MIRGraph::ComputeDefBlockMatrix() {
def_block_matrix_[i] =
new (arena_) ArenaBitVector(arena_, GetNumBlocks(), false, kBitMapBMatrix);
}
- AllNodesIterator iter(this, false /* not iterative */);
+ AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
FindLocalLiveIn(bb);
}
- AllNodesIterator iter2(this, false /* not iterative */);
+ AllNodesIterator iter2(this);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
FillDefBlockMatrix(bb);
}
@@ -169,7 +175,7 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) {
if (dom_post_order_traversal_ == NULL) {
// First time - create the array.
dom_post_order_traversal_ =
- new (arena_) GrowableArray<int>(arena_, num_reachable_blocks_,
+ new (arena_) GrowableArray<BasicBlockId>(arena_, num_reachable_blocks_,
kGrowableArrayDomPostOrderTraversal);
} else {
dom_post_order_traversal_->Reset();
@@ -177,9 +183,9 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) {
ClearAllVisitedFlags();
std::vector<std::pair<BasicBlock*, ArenaBitVector::Iterator*> > work_stack;
bb->visited = true;
- work_stack.push_back(std::make_pair(bb, new (arena_) ArenaBitVector::Iterator(bb->i_dominated)));
+ work_stack.push_back(std::make_pair(bb, bb->i_dominated->GetIterator()));
while (!work_stack.empty()) {
- std::pair<BasicBlock*, ArenaBitVector::Iterator*> curr = work_stack.back();
+ const std::pair<BasicBlock*, ArenaBitVector::Iterator*>& curr = work_stack.back();
BasicBlock* curr_bb = curr.first;
ArenaBitVector::Iterator* curr_idom_iter = curr.second;
int bb_idx = curr_idom_iter->Next();
@@ -190,14 +196,17 @@ void MIRGraph::ComputeDomPostOrderTraversal(BasicBlock* bb) {
BasicBlock* new_bb = GetBasicBlock(bb_idx);
new_bb->visited = true;
work_stack.push_back(
- std::make_pair(new_bb, new (arena_) ArenaBitVector::Iterator(new_bb->i_dominated)));
+ std::make_pair(new_bb, new_bb->i_dominated->GetIterator()));
} else {
// no successor/next
- dom_post_order_traversal_->Insert(curr_bb->id);
+ if (curr_bb->id != NullBasicBlockId) {
+ dom_post_order_traversal_->Insert(curr_bb->id);
+ }
work_stack.pop_back();
/* hacky loop detection */
- if (curr_bb->taken && curr_bb->dominators->IsBitSet(curr_bb->taken->id)) {
+ if ((curr_bb->taken != NullBasicBlockId) && curr_bb->dominators->IsBitSet(curr_bb->taken)) {
+ curr_bb->nesting_depth++;
attributes_ |= METHOD_HAS_LOOP;
}
}
@@ -210,7 +219,7 @@ void MIRGraph::CheckForDominanceFrontier(BasicBlock* dom_bb,
* TODO - evaluate whether phi will ever need to be inserted into exit
* blocks.
*/
- if (succ_bb->i_dom != dom_bb &&
+ if (succ_bb->i_dom != dom_bb->id &&
succ_bb->block_type == kDalvikByteCode &&
succ_bb->hidden == false) {
dom_bb->dom_frontier->SetBit(succ_bb->id);
@@ -220,20 +229,20 @@ void MIRGraph::CheckForDominanceFrontier(BasicBlock* dom_bb,
/* Worker function to compute the dominance frontier */
bool MIRGraph::ComputeDominanceFrontier(BasicBlock* bb) {
/* Calculate DF_local */
- if (bb->taken) {
- CheckForDominanceFrontier(bb, bb->taken);
+ if (bb->taken != NullBasicBlockId) {
+ CheckForDominanceFrontier(bb, GetBasicBlock(bb->taken));
}
- if (bb->fall_through) {
- CheckForDominanceFrontier(bb, bb->fall_through);
+ if (bb->fall_through != NullBasicBlockId) {
+ CheckForDominanceFrontier(bb, GetBasicBlock(bb->fall_through));
}
- if (bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ if (bb->successor_block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
while (true) {
SuccessorBlockInfo *successor_block_info = iterator.Next();
if (successor_block_info == NULL) {
break;
}
- BasicBlock* succ_bb = successor_block_info->block;
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
CheckForDominanceFrontier(bb, succ_bb);
}
}
@@ -306,17 +315,17 @@ int MIRGraph::FindCommonParent(int block1, int block2) {
/* Worker function to compute each block's immediate dominator */
bool MIRGraph::ComputeblockIDom(BasicBlock* bb) {
/* Special-case entry block */
- if (bb == GetEntryBlock()) {
+ if ((bb->id == NullBasicBlockId) || (bb == GetEntryBlock())) {
return false;
}
/* Iterate through the predecessors */
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
/* Find the first processed predecessor */
int idom = -1;
while (true) {
- BasicBlock* pred_bb = iter.Next();
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
CHECK(pred_bb != NULL);
if (i_dom_list_[pred_bb->dfs_id] != NOTVISITED) {
idom = pred_bb->dfs_id;
@@ -326,7 +335,7 @@ bool MIRGraph::ComputeblockIDom(BasicBlock* bb) {
/* Scan the rest of the predecessors */
while (true) {
- BasicBlock* pred_bb = iter.Next();
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) {
break;
}
@@ -352,7 +361,7 @@ bool MIRGraph::ComputeBlockDominators(BasicBlock* bb) {
if (bb == GetEntryBlock()) {
bb->dominators->ClearAllBits();
} else {
- bb->dominators->Copy(bb->i_dom->dominators);
+ bb->dominators->Copy(GetBasicBlock(bb->i_dom)->dominators);
}
bb->dominators->SetBit(bb->id);
return false;
@@ -364,7 +373,7 @@ bool MIRGraph::SetDominators(BasicBlock* bb) {
DCHECK_NE(idom_dfs_idx, NOTVISITED);
int i_dom_idx = dfs_post_order_->Get(idom_dfs_idx);
BasicBlock* i_dom = GetBasicBlock(i_dom_idx);
- bb->i_dom = i_dom;
+ bb->i_dom = i_dom->id;
/* Add bb to the i_dominated set of the immediate dominator block */
i_dom->i_dominated->SetBit(bb->id);
}
@@ -377,7 +386,7 @@ void MIRGraph::ComputeDominators() {
int num_total_blocks = GetBasicBlockListCount();
/* Initialize domination-related data structures */
- ReachableNodesIterator iter(this, false /* not iterative */);
+ PreOrderDfsIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != NULL; bb = iter.Next()) {
InitializeDominationInfo(bb);
}
@@ -396,7 +405,7 @@ void MIRGraph::ComputeDominators() {
i_dom_list_[GetEntryBlock()->dfs_id] = GetEntryBlock()->dfs_id;
/* Compute the immediate dominators */
- ReversePostOrderDfsIterator iter2(this, true /* iterative */);
+ RepeatingReversePostOrderDfsIterator iter2(this);
bool change = false;
for (BasicBlock* bb = iter2.Next(false); bb != NULL; bb = iter2.Next(change)) {
change = ComputeblockIDom(bb);
@@ -412,21 +421,21 @@ void MIRGraph::ComputeDominators() {
} else {
temp_block_v_->ClearAllBits();
}
- GetEntryBlock()->i_dom = NULL;
+ GetEntryBlock()->i_dom = 0;
- ReachableNodesIterator iter3(this, false /* not iterative */);
+ PreOrderDfsIterator iter3(this);
for (BasicBlock* bb = iter3.Next(); bb != NULL; bb = iter3.Next()) {
SetDominators(bb);
}
- ReversePostOrderDfsIterator iter4(this, false /* not iterative */);
+ ReversePostOrderDfsIterator iter4(this);
for (BasicBlock* bb = iter4.Next(); bb != NULL; bb = iter4.Next()) {
ComputeBlockDominators(bb);
}
// Compute the dominance frontier for each block.
ComputeDomPostOrderTraversal(GetEntryBlock());
- PostOrderDOMIterator iter5(this, false /* not iterative */);
+ PostOrderDOMIterator iter5(this);
for (BasicBlock* bb = iter5.Next(); bb != NULL; bb = iter5.Next()) {
ComputeDominanceFrontier(bb);
}
@@ -463,20 +472,22 @@ bool MIRGraph::ComputeBlockLiveIns(BasicBlock* bb) {
return false;
}
temp_dalvik_register_v->Copy(bb->data_flow_info->live_in_v);
- if (bb->taken && bb->taken->data_flow_info)
- ComputeSuccLineIn(temp_dalvik_register_v, bb->taken->data_flow_info->live_in_v,
+ BasicBlock* bb_taken = GetBasicBlock(bb->taken);
+ BasicBlock* bb_fall_through = GetBasicBlock(bb->fall_through);
+ if (bb_taken && bb_taken->data_flow_info)
+ ComputeSuccLineIn(temp_dalvik_register_v, bb_taken->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
- if (bb->fall_through && bb->fall_through->data_flow_info)
- ComputeSuccLineIn(temp_dalvik_register_v, bb->fall_through->data_flow_info->live_in_v,
+ if (bb_fall_through && bb_fall_through->data_flow_info)
+ ComputeSuccLineIn(temp_dalvik_register_v, bb_fall_through->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
- if (bb->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_block_list.blocks);
+ if (bb->successor_block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(bb->successor_blocks);
while (true) {
SuccessorBlockInfo *successor_block_info = iterator.Next();
if (successor_block_info == NULL) {
break;
}
- BasicBlock* succ_bb = successor_block_info->block;
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
if (succ_bb->data_flow_info) {
ComputeSuccLineIn(temp_dalvik_register_v, succ_bb->data_flow_info->live_in_v,
bb->data_flow_info->def_v);
@@ -503,7 +514,7 @@ void MIRGraph::InsertPhiNodes() {
temp_dalvik_register_v_ =
new (arena_) ArenaBitVector(arena_, cu_->num_dalvik_registers, false, kBitMapRegisterV);
- PostOrderDfsIterator iter(this, true /* iterative */);
+ RepeatingPostOrderDfsIterator iter(this);
bool change = false;
for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
change = ComputeBlockLiveIns(bb);
@@ -579,50 +590,37 @@ void MIRGraph::InsertPhiNodes() {
* predecessor blocks
*/
bool MIRGraph::InsertPhiNodeOperands(BasicBlock* bb) {
- MIR *mir;
- std::vector<int> uses;
- std::vector<int> incoming_arc;
-
/* Phi nodes are at the beginning of each block */
- for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
if (mir->dalvikInsn.opcode != static_cast<Instruction::Code>(kMirOpPhi))
return true;
int ssa_reg = mir->ssa_rep->defs[0];
DCHECK_GE(ssa_reg, 0); // Shouldn't see compiler temps here
int v_reg = SRegToVReg(ssa_reg);
- uses.clear();
- incoming_arc.clear();
-
/* Iterate through the predecessors */
- GrowableArray<BasicBlock*>::Iterator iter(bb->predecessors);
+ GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
+ size_t num_uses = bb->predecessors->Size();
+ mir->ssa_rep->num_uses = num_uses;
+ int* uses = static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses,
+ ArenaAllocator::kAllocDFInfo));
+ mir->ssa_rep->uses = uses;
+ mir->ssa_rep->fp_use =
+ static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo));
+ BasicBlockId* incoming =
+ static_cast<BasicBlockId*>(arena_->Alloc(sizeof(BasicBlockId) * num_uses,
+ ArenaAllocator::kAllocDFInfo));
+ mir->meta.phi_incoming = incoming;
+ int idx = 0;
while (true) {
- BasicBlock* pred_bb = iter.Next();
+ BasicBlock* pred_bb = GetBasicBlock(iter.Next());
if (!pred_bb) {
break;
}
int ssa_reg = pred_bb->data_flow_info->vreg_to_ssa_map[v_reg];
- uses.push_back(ssa_reg);
- incoming_arc.push_back(pred_bb->id);
- }
-
- /* Count the number of SSA registers for a Dalvik register */
- int num_uses = uses.size();
- mir->ssa_rep->num_uses = num_uses;
- mir->ssa_rep->uses =
- static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo));
- mir->ssa_rep->fp_use =
- static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_uses, ArenaAllocator::kAllocDFInfo));
- int* incoming =
- static_cast<int*>(arena_->Alloc(sizeof(int) * num_uses, ArenaAllocator::kAllocDFInfo));
- // TODO: Ugly, rework (but don't burden each MIR/LIR for Phi-only needs)
- mir->dalvikInsn.vB = reinterpret_cast<uintptr_t>(incoming);
-
- /* Set the uses array for the phi node */
- int *use_ptr = mir->ssa_rep->uses;
- for (int i = 0; i < num_uses; i++) {
- *use_ptr++ = uses[i];
- *incoming++ = incoming_arc[i];
+ uses[idx] = ssa_reg;
+ incoming[idx] = pred_bb->id;
+ idx++;
}
}
@@ -644,24 +642,24 @@ void MIRGraph::DoDFSPreOrderSSARename(BasicBlock* block) {
static_cast<int*>(arena_->Alloc(map_size, ArenaAllocator::kAllocDalvikToSSAMap));
memcpy(saved_ssa_map, vreg_to_ssa_map_, map_size);
- if (block->fall_through) {
- DoDFSPreOrderSSARename(block->fall_through);
+ if (block->fall_through != NullBasicBlockId) {
+ DoDFSPreOrderSSARename(GetBasicBlock(block->fall_through));
/* Restore SSA map snapshot */
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
}
- if (block->taken) {
- DoDFSPreOrderSSARename(block->taken);
+ if (block->taken != NullBasicBlockId) {
+ DoDFSPreOrderSSARename(GetBasicBlock(block->taken));
/* Restore SSA map snapshot */
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
}
- if (block->successor_block_list.block_list_type != kNotUsed) {
- GrowableArray<SuccessorBlockInfo*>::Iterator iterator(block->successor_block_list.blocks);
+ if (block->successor_block_list_type != kNotUsed) {
+ GrowableArray<SuccessorBlockInfo*>::Iterator iterator(block->successor_blocks);
while (true) {
SuccessorBlockInfo *successor_block_info = iterator.Next();
if (successor_block_info == NULL) {
break;
}
- BasicBlock* succ_bb = successor_block_info->block;
+ BasicBlock* succ_bb = GetBasicBlock(successor_block_info->block);
DoDFSPreOrderSSARename(succ_bb);
/* Restore SSA map snapshot */
memcpy(vreg_to_ssa_map_, saved_ssa_map, map_size);
@@ -700,7 +698,7 @@ void MIRGraph::SSATransformation() {
new (arena_) ArenaBitVector(arena_, GetNumSSARegs(), false, kBitMapTempSSARegisterV);
/* Insert phi-operands with latest SSA names from predecessor blocks */
- ReachableNodesIterator iter2(this, false /* not iterative */);
+ PreOrderDfsIterator iter2(this);
for (BasicBlock* bb = iter2.Next(); bb != NULL; bb = iter2.Next()) {
InsertPhiNodeOperands(bb);
}
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index 07f37bbbbb..32fac0b393 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -29,6 +29,16 @@ bool MIRGraph::SetFp(int index, bool is_fp) {
return change;
}
+bool MIRGraph::SetFp(int index) {
+ bool change = false;
+ if (!reg_location_[index].fp) {
+ reg_location_[index].fp = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetCore(int index, bool is_core) {
bool change = false;
if (is_core && !reg_location_[index].defined) {
@@ -39,6 +49,16 @@ bool MIRGraph::SetCore(int index, bool is_core) {
return change;
}
+bool MIRGraph::SetCore(int index) {
+ bool change = false;
+ if (!reg_location_[index].defined) {
+ reg_location_[index].core = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetRef(int index, bool is_ref) {
bool change = false;
if (is_ref && !reg_location_[index].defined) {
@@ -49,6 +69,16 @@ bool MIRGraph::SetRef(int index, bool is_ref) {
return change;
}
+bool MIRGraph::SetRef(int index) {
+ bool change = false;
+ if (!reg_location_[index].defined) {
+ reg_location_[index].ref = true;
+ reg_location_[index].defined = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetWide(int index, bool is_wide) {
bool change = false;
if (is_wide && !reg_location_[index].wide) {
@@ -58,6 +88,15 @@ bool MIRGraph::SetWide(int index, bool is_wide) {
return change;
}
+bool MIRGraph::SetWide(int index) {
+ bool change = false;
+ if (!reg_location_[index].wide) {
+ reg_location_[index].wide = true;
+ change = true;
+ }
+ return change;
+}
+
bool MIRGraph::SetHigh(int index, bool is_high) {
bool change = false;
if (is_high && !reg_location_[index].high_word) {
@@ -67,6 +106,16 @@ bool MIRGraph::SetHigh(int index, bool is_high) {
return change;
}
+bool MIRGraph::SetHigh(int index) {
+ bool change = false;
+ if (!reg_location_[index].high_word) {
+ reg_location_[index].high_word = true;
+ change = true;
+ }
+ return change;
+}
+
+
/*
* Infer types and sizes. We don't need to track change on sizes,
* as it doesn't propagate. We're guaranteed at least one pass through
@@ -84,21 +133,23 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
SSARepresentation *ssa_rep = mir->ssa_rep;
if (ssa_rep) {
int attrs = oat_data_flow_attributes_[mir->dalvikInsn.opcode];
+ const int* uses = ssa_rep->uses;
+ const int* defs = ssa_rep->defs;
// Handle defs
if (attrs & DF_DA) {
if (attrs & DF_CORE_A) {
- changed |= SetCore(ssa_rep->defs[0], true);
+ changed |= SetCore(defs[0]);
}
if (attrs & DF_REF_A) {
- changed |= SetRef(ssa_rep->defs[0], true);
+ changed |= SetRef(defs[0]);
}
if (attrs & DF_A_WIDE) {
- reg_location_[ssa_rep->defs[0]].wide = true;
- reg_location_[ssa_rep->defs[1]].wide = true;
- reg_location_[ssa_rep->defs[1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->defs[0])+1,
- SRegToVReg(ssa_rep->defs[1]));
+ reg_location_[defs[0]].wide = true;
+ reg_location_[defs[1]].wide = true;
+ reg_location_[defs[1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(defs[0])+1,
+ SRegToVReg(defs[1]));
}
}
@@ -106,17 +157,17 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
int next = 0;
if (attrs & DF_UA) {
if (attrs & DF_CORE_A) {
- changed |= SetCore(ssa_rep->uses[next], true);
+ changed |= SetCore(uses[next]);
}
if (attrs & DF_REF_A) {
- changed |= SetRef(ssa_rep->uses[next], true);
+ changed |= SetRef(uses[next]);
}
if (attrs & DF_A_WIDE) {
- reg_location_[ssa_rep->uses[next]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
- SRegToVReg(ssa_rep->uses[next + 1]));
+ reg_location_[uses[next]].wide = true;
+ reg_location_[uses[next + 1]].wide = true;
+ reg_location_[uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[next])+1,
+ SRegToVReg(uses[next + 1]));
next += 2;
} else {
next++;
@@ -124,17 +175,17 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
}
if (attrs & DF_UB) {
if (attrs & DF_CORE_B) {
- changed |= SetCore(ssa_rep->uses[next], true);
+ changed |= SetCore(uses[next]);
}
if (attrs & DF_REF_B) {
- changed |= SetRef(ssa_rep->uses[next], true);
+ changed |= SetRef(uses[next]);
}
if (attrs & DF_B_WIDE) {
- reg_location_[ssa_rep->uses[next]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
- SRegToVReg(ssa_rep->uses[next + 1]));
+ reg_location_[uses[next]].wide = true;
+ reg_location_[uses[next + 1]].wide = true;
+ reg_location_[uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[next])+1,
+ SRegToVReg(uses[next + 1]));
next += 2;
} else {
next++;
@@ -142,17 +193,17 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
}
if (attrs & DF_UC) {
if (attrs & DF_CORE_C) {
- changed |= SetCore(ssa_rep->uses[next], true);
+ changed |= SetCore(uses[next]);
}
if (attrs & DF_REF_C) {
- changed |= SetRef(ssa_rep->uses[next], true);
+ changed |= SetRef(uses[next]);
}
if (attrs & DF_C_WIDE) {
- reg_location_[ssa_rep->uses[next]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].wide = true;
- reg_location_[ssa_rep->uses[next + 1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[next])+1,
- SRegToVReg(ssa_rep->uses[next + 1]));
+ reg_location_[uses[next]].wide = true;
+ reg_location_[uses[next + 1]].wide = true;
+ reg_location_[uses[next + 1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[next])+1,
+ SRegToVReg(uses[next + 1]));
}
}
@@ -162,27 +213,27 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
(mir->dalvikInsn.opcode == Instruction::RETURN_OBJECT)) {
switch (cu_->shorty[0]) {
case 'I':
- changed |= SetCore(ssa_rep->uses[0], true);
+ changed |= SetCore(uses[0]);
break;
case 'J':
- changed |= SetCore(ssa_rep->uses[0], true);
- changed |= SetCore(ssa_rep->uses[1], true);
- reg_location_[ssa_rep->uses[0]].wide = true;
- reg_location_[ssa_rep->uses[1]].wide = true;
- reg_location_[ssa_rep->uses[1]].high_word = true;
+ changed |= SetCore(uses[0]);
+ changed |= SetCore(uses[1]);
+ reg_location_[uses[0]].wide = true;
+ reg_location_[uses[1]].wide = true;
+ reg_location_[uses[1]].high_word = true;
break;
case 'F':
- changed |= SetFp(ssa_rep->uses[0], true);
+ changed |= SetFp(uses[0]);
break;
case 'D':
- changed |= SetFp(ssa_rep->uses[0], true);
- changed |= SetFp(ssa_rep->uses[1], true);
- reg_location_[ssa_rep->uses[0]].wide = true;
- reg_location_[ssa_rep->uses[1]].wide = true;
- reg_location_[ssa_rep->uses[1]].high_word = true;
+ changed |= SetFp(uses[0]);
+ changed |= SetFp(uses[1]);
+ reg_location_[uses[0]].wide = true;
+ reg_location_[uses[1]].wide = true;
+ reg_location_[uses[1]].high_word = true;
break;
case 'L':
- changed |= SetRef(ssa_rep->uses[0], true);
+ changed |= SetRef(uses[0]);
break;
default: break;
}
@@ -206,10 +257,10 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
SSARepresentation* tgt_rep = move_result_mir->ssa_rep;
DCHECK(tgt_rep != NULL);
tgt_rep->fp_def[0] = true;
- changed |= SetFp(tgt_rep->defs[0], true);
+ changed |= SetFp(tgt_rep->defs[0]);
if (shorty[0] == 'D') {
tgt_rep->fp_def[1] = true;
- changed |= SetFp(tgt_rep->defs[1], true);
+ changed |= SetFp(tgt_rep->defs[1]);
}
}
}
@@ -217,8 +268,8 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
// If this is a non-static invoke, mark implicit "this"
if (((mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC) &&
(mir->dalvikInsn.opcode != Instruction::INVOKE_STATIC_RANGE))) {
- reg_location_[ssa_rep->uses[next]].defined = true;
- reg_location_[ssa_rep->uses[next]].ref = true;
+ reg_location_[uses[next]].defined = true;
+ reg_location_[uses[next]].ref = true;
next++;
}
uint32_t cpos = 1;
@@ -229,28 +280,28 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
case 'D':
ssa_rep->fp_use[i] = true;
ssa_rep->fp_use[i+1] = true;
- reg_location_[ssa_rep->uses[i]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1]));
+ reg_location_[uses[i]].wide = true;
+ reg_location_[uses[i+1]].wide = true;
+ reg_location_[uses[i+1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1]));
i++;
break;
case 'J':
- reg_location_[ssa_rep->uses[i]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].wide = true;
- reg_location_[ssa_rep->uses[i+1]].high_word = true;
- DCHECK_EQ(SRegToVReg(ssa_rep->uses[i])+1, SRegToVReg(ssa_rep->uses[i+1]));
- changed |= SetCore(ssa_rep->uses[i], true);
+ reg_location_[uses[i]].wide = true;
+ reg_location_[uses[i+1]].wide = true;
+ reg_location_[uses[i+1]].high_word = true;
+ DCHECK_EQ(SRegToVReg(uses[i])+1, SRegToVReg(uses[i+1]));
+ changed |= SetCore(uses[i]);
i++;
break;
case 'F':
ssa_rep->fp_use[i] = true;
break;
case 'L':
- changed |= SetRef(ssa_rep->uses[i], true);
+ changed |= SetRef(uses[i]);
break;
default:
- changed |= SetCore(ssa_rep->uses[i], true);
+ changed |= SetCore(uses[i]);
break;
}
i++;
@@ -260,11 +311,11 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) {
if (ssa_rep->fp_use[i])
- changed |= SetFp(ssa_rep->uses[i], true);
+ changed |= SetFp(uses[i]);
}
for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) {
if (ssa_rep->fp_def[i])
- changed |= SetFp(ssa_rep->defs[i], true);
+ changed |= SetFp(defs[i]);
}
// Special-case handling for moves & Phi
if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) {
@@ -276,14 +327,14 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
*/
bool is_phi = (static_cast<int>(mir->dalvikInsn.opcode) ==
kMirOpPhi);
- RegLocation rl_temp = reg_location_[ssa_rep->defs[0]];
+ RegLocation rl_temp = reg_location_[defs[0]];
bool defined_fp = rl_temp.defined && rl_temp.fp;
bool defined_core = rl_temp.defined && rl_temp.core;
bool defined_ref = rl_temp.defined && rl_temp.ref;
bool is_wide = rl_temp.wide || ((attrs & DF_A_WIDE) != 0);
bool is_high = is_phi && rl_temp.wide && rl_temp.high_word;
for (int i = 0; i < ssa_rep->num_uses; i++) {
- rl_temp = reg_location_[ssa_rep->uses[i]];
+ rl_temp = reg_location_[uses[i]];
defined_fp |= rl_temp.defined && rl_temp.fp;
defined_core |= rl_temp.defined && rl_temp.core;
defined_ref |= rl_temp.defined && rl_temp.ref;
@@ -303,26 +354,26 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb) {
<< " has both fp and core/ref uses for same def.";
cu_->disable_opt |= (1 << kPromoteRegs);
}
- changed |= SetFp(ssa_rep->defs[0], defined_fp);
- changed |= SetCore(ssa_rep->defs[0], defined_core);
- changed |= SetRef(ssa_rep->defs[0], defined_ref);
- changed |= SetWide(ssa_rep->defs[0], is_wide);
- changed |= SetHigh(ssa_rep->defs[0], is_high);
+ changed |= SetFp(defs[0], defined_fp);
+ changed |= SetCore(defs[0], defined_core);
+ changed |= SetRef(defs[0], defined_ref);
+ changed |= SetWide(defs[0], is_wide);
+ changed |= SetHigh(defs[0], is_high);
if (attrs & DF_A_WIDE) {
- changed |= SetWide(ssa_rep->defs[1], true);
- changed |= SetHigh(ssa_rep->defs[1], true);
+ changed |= SetWide(defs[1]);
+ changed |= SetHigh(defs[1]);
}
for (int i = 0; i < ssa_rep->num_uses; i++) {
- changed |= SetFp(ssa_rep->uses[i], defined_fp);
- changed |= SetCore(ssa_rep->uses[i], defined_core);
- changed |= SetRef(ssa_rep->uses[i], defined_ref);
- changed |= SetWide(ssa_rep->uses[i], is_wide);
- changed |= SetHigh(ssa_rep->uses[i], is_high);
+ changed |= SetFp(uses[i], defined_fp);
+ changed |= SetCore(uses[i], defined_core);
+ changed |= SetRef(uses[i], defined_ref);
+ changed |= SetWide(uses[i], is_wide);
+ changed |= SetHigh(uses[i], is_high);
}
if (attrs & DF_A_WIDE) {
DCHECK_EQ(ssa_rep->num_uses, 2);
- changed |= SetWide(ssa_rep->uses[1], true);
- changed |= SetHigh(ssa_rep->uses[1], true);
+ changed |= SetWide(uses[1]);
+ changed |= SetHigh(uses[1]);
}
}
}
@@ -444,7 +495,7 @@ void MIRGraph::BuildRegLocations() {
}
/* Do type & size inference pass */
- PreOrderDfsIterator iter(this, true /* iterative */);
+ RepeatingPreOrderDfsIterator iter(this);
bool change = false;
for (BasicBlock* bb = iter.Next(false); bb != NULL; bb = iter.Next(change)) {
change = InferTypeAndSize(bb);
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index b876724f21..4871e162a3 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -64,7 +64,7 @@ static void DumpStat(size_t x, size_t y, const char* str) {
if (x == 0 && y == 0) {
return;
}
- VLOG(compiler) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases";
+ LOG(INFO) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases";
}
class AOTCompilationStats {
@@ -336,10 +336,12 @@ extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver,
std::string const& filename);
CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes, size_t thread_count,
bool dump_stats)
: compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
+ instruction_set_features_(instruction_set_features),
freezing_constructor_lock_("freezing constructor lock"),
compiled_classes_lock_("compiled classes lock"),
compiled_methods_lock_("compiled method lock"),
@@ -355,7 +357,11 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet
jni_compiler_(NULL),
compiler_enable_auto_elf_loading_(NULL),
compiler_get_method_code_addr_(NULL),
- support_boot_image_fixup_(true) {
+ support_boot_image_fixup_(instruction_set == kThumb2),
+ dedupe_code_("dedupe code"),
+ dedupe_mapping_table_("dedupe mapping table"),
+ dedupe_vmap_table_("dedupe vmap table"),
+ dedupe_gc_map_("dedupe gc map") {
CHECK_PTHREAD_CALL(pthread_key_create, (&tls_key_, NULL), "compiler tls key");
@@ -465,6 +471,11 @@ const std::vector<uint8_t>* CompilerDriver::CreateJniDlsymLookup() const {
return CreateTrampoline(instruction_set_, kJniAbi, JNI_ENTRYPOINT_OFFSET(pDlsymLookup));
}
+const std::vector<uint8_t>* CompilerDriver::CreatePortableImtConflictTrampoline() const {
+ return CreateTrampoline(instruction_set_, kPortableAbi,
+ PORTABLE_ENTRYPOINT_OFFSET(pPortableImtConflictTrampoline));
+}
+
const std::vector<uint8_t>* CompilerDriver::CreatePortableResolutionTrampoline() const {
return CreateTrampoline(instruction_set_, kPortableAbi,
PORTABLE_ENTRYPOINT_OFFSET(pPortableResolutionTrampoline));
@@ -475,6 +486,11 @@ const std::vector<uint8_t>* CompilerDriver::CreatePortableToInterpreterBridge()
PORTABLE_ENTRYPOINT_OFFSET(pPortableToInterpreterBridge));
}
+const std::vector<uint8_t>* CompilerDriver::CreateQuickImtConflictTrampoline() const {
+ return CreateTrampoline(instruction_set_, kQuickAbi,
+ QUICK_ENTRYPOINT_OFFSET(pQuickImtConflictTrampoline));
+}
+
const std::vector<uint8_t>* CompilerDriver::CreateQuickResolutionTrampoline() const {
return CreateTrampoline(instruction_set_, kQuickAbi,
QUICK_ENTRYPOINT_OFFSET(pQuickResolutionTrampoline));
@@ -597,7 +613,6 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const De
}
bool CompilerDriver::IsImageClass(const char* descriptor) const {
- DCHECK(descriptor != NULL);
if (!IsImage()) {
return true;
} else {
@@ -776,7 +791,8 @@ void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) {
bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file,
uint32_t type_idx) {
- if (IsImage() && IsImageClass(dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)))) {
+ if (IsImage() &&
+ IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) {
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file);
@@ -912,9 +928,9 @@ static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod(ScopedObjectA
}
static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjectAccess& soa,
- const DexCompilationUnit* mUnit,
- uint32_t method_idx,
- InvokeType type)
+ const DexCompilationUnit* mUnit,
+ uint32_t method_idx,
+ InvokeType type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile());
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader());
@@ -923,11 +939,11 @@ static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjec
}
bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, bool& is_volatile, bool is_put) {
+ bool is_put, int* field_offset, bool* is_volatile) {
ScopedObjectAccess soa(Thread::Current());
// Conservative defaults.
- field_offset = -1;
- is_volatile = true;
+ *field_offset = -1;
+ *is_volatile = true;
// Try to resolve field and ignore if an Incompatible Class Change Error (ie is static).
mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx);
if (resolved_field != NULL && !resolved_field->IsStatic()) {
@@ -954,8 +970,8 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi
bool is_write_to_final_from_wrong_class = is_put && resolved_field->IsFinal() &&
fields_class != referrer_class;
if (access_ok && !is_write_to_final_from_wrong_class) {
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedInstanceField();
return true; // Fast path.
}
@@ -970,15 +986,14 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi
}
bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, int& ssb_index,
- bool& is_referrers_class, bool& is_volatile,
- bool is_put) {
+ bool is_put, int* field_offset, int* ssb_index,
+ bool* is_referrers_class, bool* is_volatile) {
ScopedObjectAccess soa(Thread::Current());
// Conservative defaults.
- field_offset = -1;
- ssb_index = -1;
- is_referrers_class = false;
- is_volatile = true;
+ *field_offset = -1;
+ *ssb_index = -1;
+ *is_referrers_class = false;
+ *is_volatile = true;
// Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static).
mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx);
if (resolved_field != NULL && resolved_field->IsStatic()) {
@@ -988,9 +1003,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila
if (referrer_class != NULL) {
mirror::Class* fields_class = resolved_field->GetDeclaringClass();
if (fields_class == referrer_class) {
- is_referrers_class = true; // implies no worrying about class initialization
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *is_referrers_class = true; // implies no worrying about class initialization
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedLocalStaticField();
return true; // fast path
} else {
@@ -1021,9 +1036,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila
if (fields_class->GetDexCache() == dex_cache) {
// common case where the dex cache of both the referrer and the field are the same,
// no need to search the dex file
- ssb_index = fields_class->GetDexTypeIndex();
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *ssb_index = fields_class->GetDexTypeIndex();
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedStaticField();
return true;
}
@@ -1036,9 +1051,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila
mUnit->GetDexFile()->FindTypeId(mUnit->GetDexFile()->GetIndexForStringId(*string_id));
if (type_id != NULL) {
// medium path, needs check of static storage base being initialized
- ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id);
- field_offset = resolved_field->GetOffset().Int32Value();
- is_volatile = resolved_field->IsVolatile();
+ *ssb_index = mUnit->GetDexFile()->GetIndexForTypeId(*type_id);
+ *field_offset = resolved_field->GetOffset().Int32Value();
+ *is_volatile = resolved_field->IsVolatile();
stats_->ResolvedStaticField();
return true;
}
@@ -1055,81 +1070,138 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila
return false; // Incomplete knowledge needs slow path.
}
-void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type,
+void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
+ bool no_guarantee_of_dex_cache_entry,
mirror::Class* referrer_class,
mirror::ArtMethod* method,
- uintptr_t& direct_code,
- uintptr_t& direct_method,
- bool update_stats) {
+ bool update_stats,
+ MethodReference* target_method,
+ uintptr_t* direct_code,
+ uintptr_t* direct_method) {
// For direct and static methods compute possible direct_code and direct_method values, ie
// an address for the Method* being invoked and an address of the code for that Method*.
// For interface calls compute a value for direct_method that is the interface method being
// invoked, so this can be passed to the out-of-line runtime support code.
- direct_code = 0;
- direct_method = 0;
+ *direct_code = 0;
+ *direct_method = 0;
+ bool use_dex_cache = false;
+ bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1;
if (compiler_backend_ == kPortable) {
if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
+ use_dex_cache = true;
} else {
- if (sharp_type != kStatic && sharp_type != kDirect && sharp_type != kInterface) {
+ if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
+ // TODO: support patching on all architectures.
+ use_dex_cache = compiling_boot && !support_boot_image_fixup_;
}
- bool method_code_in_boot = method->GetDeclaringClass()->GetClassLoader() == NULL;
- if (!method_code_in_boot) {
- return;
+ bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
+ if (!use_dex_cache) {
+ if (!method_code_in_boot) {
+ use_dex_cache = true;
+ } else {
+ bool has_clinit_trampoline =
+ method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
+ if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
+ // Ensure we run the clinit trampoline unless we are invoking a static method in the same
+ // class.
+ use_dex_cache = true;
+ }
+ }
}
- bool has_clinit_trampoline = method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
- if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
- // Ensure we run the clinit trampoline unless we are invoking a static method in the same class.
- return;
+ if (update_stats && method_code_in_boot) {
+ stats_->DirectCallsToBoot(*type);
+ stats_->DirectMethodsToBoot(*type);
}
- if (update_stats) {
- if (sharp_type != kInterface) { // Interfaces always go via a trampoline.
- stats_->DirectCallsToBoot(type);
+ if (!use_dex_cache && compiling_boot) {
+ MethodHelper mh(method);
+ if (!IsImageClass(mh.GetDeclaringClassDescriptor())) {
+ // We can only branch directly to Methods that are resolved in the DexCache.
+ // Otherwise we won't invoke the resolution trampoline.
+ use_dex_cache = true;
}
- stats_->DirectMethodsToBoot(type);
}
- bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1;
- if (compiling_boot) {
- if (support_boot_image_fixup_) {
- MethodHelper mh(method);
- if (IsImageClass(mh.GetDeclaringClassDescriptor())) {
- // We can only branch directly to Methods that are resolved in the DexCache.
- // Otherwise we won't invoke the resolution trampoline.
- direct_method = -1;
- direct_code = -1;
+ // The method is defined not within this dex file. We need a dex cache slot within the current
+ // dex file or direct pointers.
+ bool must_use_direct_pointers = false;
+ if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) {
+ target_method->dex_method_index = method->GetDexMethodIndex();
+ } else {
+ // TODO: support patching from one dex file to another in the boot image.
+ use_dex_cache = use_dex_cache || compiling_boot;
+ if (no_guarantee_of_dex_cache_entry) {
+ // See if the method is also declared in this dex cache.
+ uint32_t dex_method_idx = MethodHelper(method).FindDexMethodIndexInOtherDexFile(
+ *referrer_class->GetDexCache()->GetDexFile());
+ if (dex_method_idx != DexFile::kDexNoIndex) {
+ target_method->dex_method_index = dex_method_idx;
+ } else {
+ must_use_direct_pointers = true;
}
}
+ }
+ if (use_dex_cache) {
+ if (must_use_direct_pointers) {
+ // Fail. Test above showed the only safe dispatch was via the dex cache, however, the direct
+ // pointers are required as the dex cache lacks an appropriate entry.
+ VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
+ } else {
+ *type = sharp_type;
+ }
} else {
- if (Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace()) {
- direct_method = reinterpret_cast<uintptr_t>(method);
+ if (compiling_boot) {
+ *type = sharp_type;
+ *direct_method = -1;
+ *direct_code = -1;
+ } else {
+ bool method_in_image =
+ Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
+ if (method_in_image) {
+ CHECK(!method->IsAbstract());
+ *type = sharp_type;
+ *direct_method = reinterpret_cast<uintptr_t>(method);
+ *direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ target_method->dex_method_index = method->GetDexMethodIndex();
+ } else if (!must_use_direct_pointers) {
+ // Set the code and rely on the dex cache for the method.
+ *type = sharp_type;
+ *direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
+ } else {
+ // Direct pointers were required but none were available.
+ VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
+ }
}
- direct_code = reinterpret_cast<uintptr_t>(method->GetEntryPointFromCompiledCode());
}
}
bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
- InvokeType& invoke_type,
- MethodReference& target_method,
- int& vtable_idx,
- uintptr_t& direct_code, uintptr_t& direct_method,
- bool update_stats) {
+ bool update_stats, bool enable_devirtualization,
+ InvokeType* invoke_type, MethodReference* target_method,
+ int* vtable_idx, uintptr_t* direct_code,
+ uintptr_t* direct_method) {
ScopedObjectAccess soa(Thread::Current());
- vtable_idx = -1;
- direct_code = 0;
- direct_method = 0;
+ *vtable_idx = -1;
+ *direct_code = 0;
+ *direct_method = 0;
mirror::ArtMethod* resolved_method =
- ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method.dex_method_index,
- invoke_type);
+ ComputeMethodReferencedFromCompilingMethod(soa, mUnit, target_method->dex_method_index,
+ *invoke_type);
if (resolved_method != NULL) {
+ if (*invoke_type == kVirtual || *invoke_type == kSuper) {
+ *vtable_idx = resolved_method->GetMethodIndex();
+ } else if (*invoke_type == kInterface) {
+ *vtable_idx = resolved_method->GetDexMethodIndex();
+ }
// Don't try to fast-path if we don't understand the caller's class or this appears to be an
// Incompatible Class Change Error.
mirror::Class* referrer_class =
ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(),
mUnit);
- bool icce = resolved_method->CheckIncompatibleClassChange(invoke_type);
+ bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type);
if (referrer_class != NULL && !icce) {
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
if (!referrer_class->CanAccess(methods_class) ||
@@ -1140,42 +1212,43 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui
// method public. Resort to the dex file to determine the correct class for the access
// check.
uint16_t class_idx =
- target_method.dex_file->GetMethodId(target_method.dex_method_index).class_idx_;
- methods_class = mUnit->GetClassLinker()->ResolveType(*target_method.dex_file,
+ target_method->dex_file->GetMethodId(target_method->dex_method_index).class_idx_;
+ methods_class = mUnit->GetClassLinker()->ResolveType(*target_method->dex_file,
class_idx, referrer_class);
}
if (referrer_class->CanAccess(methods_class) &&
referrer_class->CanAccessMember(methods_class, resolved_method->GetAccessFlags())) {
- const bool kEnableFinalBasedSharpening = true;
+ const bool enableFinalBasedSharpening = enable_devirtualization;
// Sharpen a virtual call into a direct call when the target is known not to have been
// overridden (ie is final).
bool can_sharpen_virtual_based_on_type =
- (invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal());
+ (*invoke_type == kVirtual) && (resolved_method->IsFinal() || methods_class->IsFinal());
// For invoke-super, ensure the vtable index will be correct to dispatch in the vtable of
// the super class.
- bool can_sharpen_super_based_on_type = (invoke_type == kSuper) &&
+ bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) &&
(referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() &&
(methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method);
- if (kEnableFinalBasedSharpening && (can_sharpen_virtual_based_on_type ||
+ if (enableFinalBasedSharpening && (can_sharpen_virtual_based_on_type ||
can_sharpen_super_based_on_type)) {
// Sharpen a virtual call into a direct call. The method_idx is into referrer's
// dex cache, check that this resolved method is where we expect it.
- CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method.dex_method_index) ==
+ CHECK(referrer_class->GetDexCache()->GetResolvedMethod(target_method->dex_method_index) ==
resolved_method) << PrettyMethod(resolved_method);
- if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
- stats_->VirtualMadeDirect(invoke_type);
+ InvokeType orig_invoke_type = *invoke_type;
+ GetCodeAndMethodForDirectCall(invoke_type, kDirect, false, referrer_class, resolved_method,
+ update_stats, target_method, direct_code, direct_method);
+ if (update_stats && (*invoke_type == kDirect)) {
+ stats_->ResolvedMethod(orig_invoke_type);
+ stats_->VirtualMadeDirect(orig_invoke_type);
}
- GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, resolved_method,
- direct_code, direct_method, update_stats);
- invoke_type = kDirect;
+ DCHECK_NE(*invoke_type, kSuper) << PrettyMethod(resolved_method);
return true;
}
- const bool kEnableVerifierBasedSharpening = true;
- if (kEnableVerifierBasedSharpening && (invoke_type == kVirtual ||
- invoke_type == kInterface)) {
+ const bool enableVerifierBasedSharpening = enable_devirtualization;
+ if (enableVerifierBasedSharpening && (*invoke_type == kVirtual ||
+ *invoke_type == kInterface)) {
// Did the verifier record a more precise invoke target based on its type information?
const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex());
const MethodReference* devirt_map_target =
@@ -1192,88 +1265,27 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui
kVirtual);
CHECK(called_method != NULL);
CHECK(!called_method->IsAbstract());
- GetCodeAndMethodForDirectCall(invoke_type, kDirect, referrer_class, called_method,
- direct_code, direct_method, update_stats);
- bool compiler_needs_dex_cache =
- (GetCompilerBackend() == kPortable) ||
- (GetCompilerBackend() == kQuick && instruction_set_ != kThumb2) ||
- (direct_code == 0) || (direct_code == static_cast<unsigned int>(-1)) ||
- (direct_method == 0) || (direct_method == static_cast<unsigned int>(-1));
- if ((devirt_map_target->dex_file != target_method.dex_file) &&
- compiler_needs_dex_cache) {
- // We need to use the dex cache to find either the method or code, and the dex file
- // containing the method isn't the one expected for the target method. Try to find
- // the method within the expected target dex file.
- // TODO: the -1 could be handled as direct code if the patching new the target dex
- // file.
- // TODO: quick only supports direct pointers with Thumb2.
- // TODO: the following should be factored into a common helper routine to find
- // one dex file's method within another.
- const DexFile* dexfile = target_method.dex_file;
- const DexFile* cm_dexfile =
- called_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
- const DexFile::MethodId& cm_method_id =
- cm_dexfile->GetMethodId(called_method->GetDexMethodIndex());
- const char* cm_descriptor = cm_dexfile->StringByTypeIdx(cm_method_id.class_idx_);
- const DexFile::StringId* descriptor = dexfile->FindStringId(cm_descriptor);
- if (descriptor != NULL) {
- const DexFile::TypeId* type_id =
- dexfile->FindTypeId(dexfile->GetIndexForStringId(*descriptor));
- if (type_id != NULL) {
- const char* cm_name = cm_dexfile->GetMethodName(cm_method_id);
- const DexFile::StringId* name = dexfile->FindStringId(cm_name);
- if (name != NULL) {
- uint16_t return_type_idx;
- std::vector<uint16_t> param_type_idxs;
- bool success = dexfile->CreateTypeList(&return_type_idx, &param_type_idxs,
- cm_dexfile->GetMethodSignature(cm_method_id));
- if (success) {
- const DexFile::ProtoId* sig =
- dexfile->FindProtoId(return_type_idx, param_type_idxs);
- if (sig != NULL) {
- const DexFile::MethodId* method_id = dexfile->FindMethodId(*type_id,
- *name, *sig);
- if (method_id != NULL) {
- if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
- stats_->VirtualMadeDirect(invoke_type);
- stats_->PreciseTypeDevirtualization();
- }
- target_method.dex_method_index = dexfile->GetIndexForMethodId(*method_id);
- invoke_type = kDirect;
- return true;
- }
- }
- }
- }
- }
- }
- // TODO: the stats for direct code and method are off as we failed to find the direct
- // method in the referring method's dex cache/file.
- } else {
- if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
- stats_->VirtualMadeDirect(invoke_type);
- stats_->PreciseTypeDevirtualization();
- }
- target_method = *devirt_map_target;
- invoke_type = kDirect;
- return true;
+ InvokeType orig_invoke_type = *invoke_type;
+ GetCodeAndMethodForDirectCall(invoke_type, kDirect, true, referrer_class, called_method,
+ update_stats, target_method, direct_code, direct_method);
+ if (update_stats && (*invoke_type == kDirect)) {
+ stats_->ResolvedMethod(orig_invoke_type);
+ stats_->VirtualMadeDirect(orig_invoke_type);
+ stats_->PreciseTypeDevirtualization();
}
+ DCHECK_NE(*invoke_type, kSuper);
+ return true;
}
}
- if (invoke_type == kSuper) {
+ if (*invoke_type == kSuper) {
// Unsharpened super calls are suspicious so go slow-path.
} else {
// Sharpening failed so generate a regular resolved method dispatch.
if (update_stats) {
- stats_->ResolvedMethod(invoke_type);
+ stats_->ResolvedMethod(*invoke_type);
}
- if (invoke_type == kVirtual || invoke_type == kSuper) {
- vtable_idx = resolved_method->GetMethodIndex();
- }
- GetCodeAndMethodForDirectCall(invoke_type, invoke_type, referrer_class, resolved_method,
- direct_code, direct_method, update_stats);
+ GetCodeAndMethodForDirectCall(invoke_type, *invoke_type, false, referrer_class, resolved_method,
+ update_stats, target_method, direct_code, direct_method);
return true;
}
}
@@ -1284,7 +1296,7 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui
soa.Self()->ClearException();
}
if (update_stats) {
- stats_->UnresolvedMethod(invoke_type);
+ stats_->UnresolvedMethod(*invoke_type);
}
return false; // Incomplete knowledge needs slow path.
}
@@ -1569,8 +1581,8 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i
CHECK(soa.Self()->IsExceptionPending());
mirror::Throwable* exception = soa.Self()->GetException(NULL);
VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
- if (strcmp(ClassHelper(exception->GetClass()).GetDescriptor(),
- "Ljava/lang/OutOfMemoryError;") == 0) {
+ if (strcmp("Ljava/lang/OutOfMemoryError;",
+ ClassHelper(exception->GetClass()).GetDescriptor()) == 0) {
// There's little point continuing compilation if the heap is exhausted.
LOG(FATAL) << "Out of memory during type resolution for compilation";
}
@@ -1589,13 +1601,11 @@ void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_fil
if (IsImage()) {
// For images we resolve all types, such as array, whereas for applications just those with
// classdefs are resolved by ResolveClassFieldsAndMethods.
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
+ timings.NewSplit("Resolve Types");
context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
}
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str()));
+ timings.NewSplit("Resolve MethodsAndFields");
context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_);
}
@@ -1658,8 +1668,7 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_
void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str()));
+ timings.NewSplit("Verify Dex File");
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_);
@@ -2086,10 +2095,13 @@ static const char* class_initializer_black_list[] = {
static void InitializeClass(const ParallelCompilationManager* manager, size_t class_def_index)
LOCKS_EXCLUDED(Locks::mutator_lock_) {
ATRACE_CALL();
- const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index);
+ const DexFile* dex_file = manager->GetDexFile();
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ const DexFile::TypeId& class_type_id = dex_file->GetTypeId(class_def.class_idx_);
+ const char* descriptor = dex_file->StringDataByIdx(class_type_id.descriptor_idx_);
+
ScopedObjectAccess soa(Thread::Current());
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
- const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def);
mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader);
if (klass != NULL) {
// Only try to initialize classes that were successfully verified.
@@ -2120,7 +2132,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl
bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;");
if (!is_black_listed) {
for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
- if (StringPiece(descriptor) == class_initializer_black_list[i]) {
+ if (strcmp(descriptor, class_initializer_black_list[i]) == 0) {
is_black_listed = true;
break;
}
@@ -2128,7 +2140,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl
}
if (!is_black_listed) {
VLOG(compiler) << "Initializing: " << descriptor;
- if (StringPiece(descriptor) == "Ljava/lang/Void;") {
+ if (strcmp("Ljava/lang/Void;", descriptor) == 0) {
// Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
ObjectLock lock(soa.Self(), klass);
mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
@@ -2159,8 +2171,7 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl
void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str()));
+ timings.NewSplit("InitializeNoClinit");
#ifndef NDEBUG
// Sanity check blacklist descriptors.
if (IsImage()) {
@@ -2267,8 +2278,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz
void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
ThreadPool& thread_pool, base::TimingLogger& timings) {
- // TODO: strdup memory leak.
- timings.NewSplit(strdup(("Compile " + dex_file.GetLocation()).c_str()));
+ timings.NewSplit("Compile Dex File");
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
&dex_file, thread_pool);
context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 3852acfd3b..9321f06526 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -91,6 +91,7 @@ class CompilerDriver {
// can assume will be in the image, with NULL implying all available
// classes.
explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
bool image, DescriptorSet* image_classes,
size_t thread_count, bool dump_stats);
@@ -104,10 +105,14 @@ class CompilerDriver {
void CompileOne(const mirror::ArtMethod* method, base::TimingLogger& timings)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- InstructionSet GetInstructionSet() const {
+ const InstructionSet& GetInstructionSet() const {
return instruction_set_;
}
+ const InstructionSetFeatures& GetInstructionSetFeatures() const {
+ return instruction_set_features_;
+ }
+
CompilerBackend GetCompilerBackend() const {
return compiler_backend_;
}
@@ -130,10 +135,14 @@ class CompilerDriver {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateJniDlsymLookup() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreatePortableImtConflictTrampoline() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreatePortableResolutionTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreatePortableToInterpreterBridge() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const std::vector<uint8_t>* CreateQuickImtConflictTrampoline() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickResolutionTrampoline() const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const std::vector<uint8_t>* CreateQuickToInterpreterBridge() const
@@ -170,22 +179,23 @@ class CompilerDriver {
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fast path instance field access? Computes field's offset and volatility.
- bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, bool& is_volatile, bool is_put)
+ bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
+ int* field_offset, bool* is_volatile)
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fastpath static field access? Computes field's offset, volatility and whether the
// field is within the referrer (which can avoid checking class initialization).
- bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
- int& field_offset, int& ssb_index,
- bool& is_referrers_class, bool& is_volatile, bool is_put)
+ bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
+ int* field_offset, int* ssb_index,
+ bool* is_referrers_class, bool* is_volatile)
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fastpath a interface, super class or virtual method call? Computes method's vtable
// index.
bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc,
- InvokeType& type, MethodReference& target_method, int& vtable_idx,
- uintptr_t& direct_code, uintptr_t& direct_method, bool update_stats)
+ bool update_stats, bool enable_devirtualization,
+ InvokeType* type, MethodReference* target_method, int* vtable_idx,
+ uintptr_t* direct_code, uintptr_t* direct_method)
LOCKS_EXCLUDED(Locks::mutator_lock_);
bool IsSafeCast(const MethodReference& mr, uint32_t dex_pc);
@@ -320,11 +330,13 @@ class CompilerDriver {
private:
// Compute constant code and method pointers when possible
- void GetCodeAndMethodForDirectCall(InvokeType type, InvokeType sharp_type,
+ void GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
+ bool no_guarantee_of_dex_cache_entry,
mirror::Class* referrer_class,
mirror::ArtMethod* method,
- uintptr_t& direct_code, uintptr_t& direct_method,
- bool update_stats)
+ bool update_stats,
+ MethodReference* target_method,
+ uintptr_t* direct_code, uintptr_t* direct_method)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
@@ -379,7 +391,8 @@ class CompilerDriver {
CompilerBackend compiler_backend_;
- InstructionSet instruction_set_;
+ const InstructionSet instruction_set_;
+ const InstructionSetFeatures instruction_set_features_;
// All class references that require
mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -458,27 +471,40 @@ class CompilerDriver {
class DedupeHashFunc {
public:
size_t operator()(const std::vector<uint8_t>& array) const {
- // Take a random sample of bytes.
+ // For small arrays compute a hash using every byte.
static const size_t kSmallArrayThreshold = 16;
- static const size_t kRandomHashCount = 16;
- size_t hash = 0;
- if (array.size() < kSmallArrayThreshold) {
- for (auto c : array) {
- hash = hash * 54 + c;
+ size_t hash = 0x811c9dc5;
+ if (array.size() <= kSmallArrayThreshold) {
+ for (uint8_t b : array) {
+ hash = (hash * 16777619) ^ b;
}
} else {
- for (size_t i = 0; i < kRandomHashCount; ++i) {
+ // For larger arrays use the 2 bytes at 6 bytes (the location of a push registers
+ // instruction field for quick generated code on ARM) and then select a number of other
+ // values at random.
+ static const size_t kRandomHashCount = 16;
+ for (size_t i = 0; i < 2; ++i) {
+ uint8_t b = array[i + 6];
+ hash = (hash * 16777619) ^ b;
+ }
+ for (size_t i = 2; i < kRandomHashCount; ++i) {
size_t r = i * 1103515245 + 12345;
- hash = hash * 54 + array[r % array.size()];
+ uint8_t b = array[r % array.size()];
+ hash = (hash * 16777619) ^ b;
}
}
+ hash += hash << 13;
+ hash ^= hash >> 7;
+ hash += hash << 3;
+ hash ^= hash >> 17;
+ hash += hash << 5;
return hash;
}
};
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_code_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_mapping_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_vmap_table_;
- DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc> dedupe_gc_map_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_code_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_mapping_table_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_vmap_table_;
+ DedupeSet<std::vector<uint8_t>, size_t, DedupeHashFunc, 4> dedupe_gc_map_;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/elf_fixup.cc b/compiler/elf_fixup.cc
index 359c4936a6..c5712880c1 100644
--- a/compiler/elf_fixup.cc
+++ b/compiler/elf_fixup.cc
@@ -27,8 +27,9 @@ namespace art {
static const bool DEBUG_FIXUP = false;
bool ElfFixup::Fixup(File* file, uintptr_t oat_data_begin) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
- CHECK(elf_file.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
+ CHECK(elf_file.get() != nullptr) << error_msg;
// Lookup "oatdata" symbol address.
::llvm::ELF::Elf32_Addr oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
diff --git a/compiler/elf_stripper.cc b/compiler/elf_stripper.cc
index 7fc662ca1d..7ee8d3cae1 100644
--- a/compiler/elf_stripper.cc
+++ b/compiler/elf_stripper.cc
@@ -27,9 +27,11 @@
namespace art {
-bool ElfStripper::Strip(File* file) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false));
- CHECK(elf_file.get() != NULL);
+bool ElfStripper::Strip(File* file, std::string* error_msg) {
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
+ if (elf_file.get() == nullptr) {
+ return false;
+ }
// ELF files produced by MCLinker look roughly like this
//
@@ -120,7 +122,8 @@ bool ElfStripper::Strip(File* file) {
elf_file->GetHeader().e_shoff = shoff;
int result = ftruncate(file->Fd(), offset);
if (result != 0) {
- PLOG(ERROR) << "Failed to truncate while stripping ELF file: " << file->GetPath();
+ *error_msg = StringPrintf("Failed to truncate while stripping ELF file: '%s': %s",
+ file->GetPath().c_str(), strerror(errno));
return false;
}
return true;
diff --git a/compiler/elf_stripper.h b/compiler/elf_stripper.h
index 6015b30cb2..f1a1d4605d 100644
--- a/compiler/elf_stripper.h
+++ b/compiler/elf_stripper.h
@@ -17,6 +17,8 @@
#ifndef ART_COMPILER_ELF_STRIPPER_H_
#define ART_COMPILER_ELF_STRIPPER_H_
+#include <string>
+
#include "base/macros.h"
#include "os.h"
@@ -26,7 +28,7 @@ class ElfStripper {
public:
// Strip an ELF file of unneeded debugging information.
// Returns true on success, false on failure.
- static bool Strip(File* file);
+ static bool Strip(File* file, std::string* error_msg);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ElfStripper);
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
index d3c13dd791..0bfe4a424c 100644
--- a/compiler/elf_writer.cc
+++ b/compiler/elf_writer.cc
@@ -47,8 +47,9 @@ llvm::ELF::Elf32_Addr ElfWriter::GetOatDataAddress(ElfFile* elf_file) {
void ElfWriter::GetOatElfInformation(File* file,
size_t& oat_loaded_size,
size_t& oat_data_offset) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(file, false, false));
- CHECK(elf_file.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(file, false, false, &error_msg));
+ CHECK(elf_file.get() != NULL) << error_msg;
oat_loaded_size = elf_file->GetLoadedSize();
CHECK_NE(0U, oat_loaded_size);
diff --git a/compiler/elf_writer_mclinker.cc b/compiler/elf_writer_mclinker.cc
index e496ace27a..8e19ef6195 100644
--- a/compiler/elf_writer_mclinker.cc
+++ b/compiler/elf_writer_mclinker.cc
@@ -153,8 +153,9 @@ void ElfWriterMclinker::Init() {
void ElfWriterMclinker::AddOatInput(std::vector<uint8_t>& oat_contents) {
// Add an artificial memory input. Based on LinkerTest.
- UniquePtr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath()));
- CHECK(oat_file.get() != NULL) << elf_file_->GetPath();
+ std::string error_msg;
+ UniquePtr<OatFile> oat_file(OatFile::OpenMemory(oat_contents, elf_file_->GetPath(), &error_msg));
+ CHECK(oat_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
const char* oat_data_start = reinterpret_cast<const char*>(&oat_file->GetOatHeader());
const size_t oat_data_length = oat_file->GetOatHeader().GetExecutableOffset();
@@ -344,8 +345,9 @@ bool ElfWriterMclinker::Link() {
#if defined(ART_USE_PORTABLE_COMPILER)
void ElfWriterMclinker::FixupOatMethodOffsets(const std::vector<const DexFile*>& dex_files) {
- UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false));
- CHECK(elf_file.get() != NULL) << elf_file_->GetPath();
+ std::string error_msg;
+ UniquePtr<ElfFile> elf_file(ElfFile::Open(elf_file_, true, false, &error_msg));
+ CHECK(elf_file.get() != NULL) << elf_file_->GetPath() << ": " << error_msg;
llvm::ELF::Elf32_Addr oatdata_address = GetOatDataAddress(elf_file.get());
DexMethodIterator it(dex_files);
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index ffe1f72926..eca67a8a6e 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -65,23 +65,26 @@ TEST_F(ElfWriterTest, dlsym) {
UniquePtr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
ASSERT_TRUE(file.get() != NULL);
{
- UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false));
- CHECK(ef.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", false);
}
{
- UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false));
- CHECK(ef.get() != NULL);
+ std::string error_msg;
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatlastword, "oatlastword", true);
}
{
- UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, true));
- CHECK(ef.get() != NULL);
- ef->Load(false);
+ std::string error_msg;
+ UniquePtr<ElfFile> ef(ElfFile::Open(file.get(), false, true, &error_msg));
+ CHECK(ef.get() != nullptr) << error_msg;
+ CHECK(ef->Load(false, &error_msg)) << error_msg;
EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata"));
EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec"));
EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword"));
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 6464a4c78e..a8b7c881f4 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -23,6 +23,8 @@
#include "compiler/oat_writer.h"
#include "gc/space/image_space.h"
#include "image.h"
+#include "lock_word.h"
+#include "mirror/object-inl.h"
#include "signal_catcher.h"
#include "UniquePtr.h"
#include "utils.h"
@@ -110,8 +112,11 @@ TEST_F(ImageTest, WriteRead) {
runtime_.reset();
java_lang_dex_file_ = NULL;
- UniquePtr<const DexFile> dex(DexFile::Open(GetLibCoreDexFileName(), GetLibCoreDexFileName()));
- ASSERT_TRUE(dex.get() != NULL);
+ std::string error_msg;
+ UniquePtr<const DexFile> dex(DexFile::Open(GetLibCoreDexFileName().c_str(),
+ GetLibCoreDexFileName().c_str(),
+ &error_msg));
+ ASSERT_TRUE(dex.get() != nullptr) << error_msg;
// Remove the reservation of the memory for use to load the image.
UnreserveImageSpace();
@@ -158,7 +163,7 @@ TEST_F(ImageTest, WriteRead) {
// non image classes should be in a space after the image.
EXPECT_GT(reinterpret_cast<byte*>(klass), image_end) << descriptor;
}
- EXPECT_TRUE(Monitor::IsValidLockWord(*klass->GetRawLockWordAddress()));
+ EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord()));
}
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f82c6fb40f..75be2c9c43 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -36,6 +36,7 @@
#include "globals.h"
#include "image.h"
#include "intern_table.h"
+#include "lock_word.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/array-inl.h"
@@ -82,12 +83,14 @@ bool ImageWriter::Write(const std::string& image_filename,
LOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
return false;
}
- oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location);
- if (oat_file_ == NULL) {
- LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location;
+ std::string error_msg;
+ oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location, &error_msg);
+ if (oat_file_ == nullptr) {
+ LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
+ << ": " << error_msg;
return false;
}
- class_linker->RegisterOatFile(*oat_file_);
+ CHECK_EQ(class_linker->RegisterOatFile(oat_file_), oat_file_);
interpreter_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetInterpreterToInterpreterBridgeOffset();
@@ -96,11 +99,15 @@ bool ImageWriter::Write(const std::string& image_filename,
jni_dlsym_lookup_offset_ = oat_file_->GetOatHeader().GetJniDlsymLookupOffset();
+ portable_imt_conflict_trampoline_offset_ =
+ oat_file_->GetOatHeader().GetPortableImtConflictTrampolineOffset();
portable_resolution_trampoline_offset_ =
oat_file_->GetOatHeader().GetPortableResolutionTrampolineOffset();
portable_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetPortableToInterpreterBridgeOffset();
+ quick_imt_conflict_trampoline_offset_ =
+ oat_file_->GetOatHeader().GetQuickImtConflictTrampolineOffset();
quick_resolution_trampoline_offset_ =
oat_file_->GetOatHeader().GetQuickResolutionTrampolineOffset();
quick_to_interpreter_bridge_offset_ =
@@ -192,9 +199,10 @@ bool ImageWriter::AllocMemory() {
int prot = PROT_READ | PROT_WRITE;
size_t length = RoundUp(size, kPageSize);
- image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot));
- if (image_.get() == NULL) {
- LOG(ERROR) << "Failed to allocate memory for image file generation";
+ std::string error_msg;
+ image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot, &error_msg));
+ if (UNLIKELY(image_.get() == nullptr)) {
+ LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg;
return false;
}
return true;
@@ -387,6 +395,8 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const {
ObjectArray<Object>::Alloc(self, object_array_class,
ImageHeader::kImageRootsMax));
image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod());
+ image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod());
+ image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt());
image_roots->Set(ImageHeader::kCalleeSaveMethod,
runtime->GetCalleeSaveMethod(Runtime::kSaveAll));
image_roots->Set(ImageHeader::kRefsOnlySaveMethod,
@@ -486,7 +496,35 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) {
DCHECK_LT(offset + n, image_writer->image_->Size());
memcpy(dst, src, n);
Object* copy = reinterpret_cast<Object*>(dst);
- copy->SetField32(Object::MonitorOffset(), 0, false); // We may have inflated the lock during compilation.
+ // Write in a hash code of objects which have inflated monitors or a hash code in their monitor
+ // word.
+ LockWord lw(copy->GetLockWord());
+ switch (lw.GetState()) {
+ case LockWord::kFatLocked: {
+ Monitor* monitor = lw.FatLockMonitor();
+ CHECK(monitor != nullptr);
+ CHECK(!monitor->IsLocked());
+ if (monitor->HasHashCode()) {
+ copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode()));
+ } else {
+ copy->SetLockWord(LockWord());
+ }
+ break;
+ }
+ case LockWord::kThinLocked: {
+ LOG(FATAL) << "Thin locked object " << obj << " found during object copy";
+ break;
+ }
+ case LockWord::kUnlocked:
+ break;
+ case LockWord::kHashCode:
+ // Do nothing since we can just keep the same hash code.
+ CHECK_NE(lw.GetHashCode(), 0);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable.";
+ break;
+ }
image_writer->FixupObject(obj, copy);
}
@@ -524,6 +562,12 @@ void ImageWriter::FixupMethod(const ArtMethod* orig, ArtMethod* copy) {
#else
copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_resolution_trampoline_offset_));
#endif
+ } else if (UNLIKELY(orig == Runtime::Current()->GetImtConflictMethod())) {
+#if defined(ART_USE_PORTABLE_COMPILER)
+ copy->SetEntryPointFromCompiledCode(GetOatAddress(portable_imt_conflict_trampoline_offset_));
+#else
+ copy->SetEntryPointFromCompiledCode(GetOatAddress(quick_imt_conflict_trampoline_offset_));
+#endif
} else {
// We assume all methods have code. If they don't currently then we set them to the use the
// resolution trampoline. Abstract methods never have code and so we need to make sure their
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 0d85f36a5b..0b408e85cc 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -40,7 +40,8 @@ class ImageWriter {
explicit ImageWriter(const CompilerDriver& compiler_driver)
: compiler_driver_(compiler_driver), oat_file_(NULL), image_end_(0), image_begin_(NULL),
oat_data_begin_(NULL), interpreter_to_interpreter_bridge_offset_(0),
- interpreter_to_compiled_code_bridge_offset_(0), portable_resolution_trampoline_offset_(0),
+ interpreter_to_compiled_code_bridge_offset_(0), portable_imt_conflict_trampoline_offset_(0),
+ portable_resolution_trampoline_offset_(0), quick_imt_conflict_trampoline_offset_(0),
quick_resolution_trampoline_offset_(0) {}
~ImageWriter() {}
@@ -204,8 +205,10 @@ class ImageWriter {
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
+ uint32_t portable_imt_conflict_trampoline_offset_;
uint32_t portable_resolution_trampoline_offset_;
uint32_t portable_to_interpreter_bridge_offset_;
+ uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index a653ab42a9..667b913039 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -152,7 +152,7 @@ TEST_F(JniCompilerTest, CompileAndRunIntMethodThroughStub) {
std::string reason;
ASSERT_TRUE(
Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode<mirror::ClassLoader*>(class_loader_),
- reason)) << reason;
+ &reason)) << reason;
jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
EXPECT_EQ(25, result);
@@ -167,7 +167,7 @@ TEST_F(JniCompilerTest, CompileAndRunStaticIntMethodThroughStub) {
std::string reason;
ASSERT_TRUE(
Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode<mirror::ClassLoader*>(class_loader_),
- reason)) << reason;
+ &reason)) << reason;
jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
EXPECT_EQ(43, result);
diff --git a/compiler/jni/portable/jni_compiler.cc b/compiler/jni/portable/jni_compiler.cc
index 43408a7d64..0c14346ad8 100644
--- a/compiler/jni/portable/jni_compiler.cc
+++ b/compiler/jni/portable/jni_compiler.cc
@@ -50,9 +50,9 @@ using ::art::llvm::runtime_support::JniMethodStartSynchronized;
using ::art::llvm::runtime_support::RuntimeId;
JniCompiler::JniCompiler(LlvmCompilationUnit* cunit,
- CompilerDriver& driver,
+ CompilerDriver* driver,
const DexCompilationUnit* dex_compilation_unit)
- : cunit_(cunit), driver_(&driver), module_(cunit_->GetModule()),
+ : cunit_(cunit), driver_(driver), module_(cunit_->GetModule()),
context_(cunit_->GetLLVMContext()), irb_(*cunit_->GetIRBuilder()),
dex_compilation_unit_(dex_compilation_unit),
func_(NULL), elf_func_idx_(0) {
diff --git a/compiler/jni/portable/jni_compiler.h b/compiler/jni/portable/jni_compiler.h
index d20c63bc1e..ffabfe61c2 100644
--- a/compiler/jni/portable/jni_compiler.h
+++ b/compiler/jni/portable/jni_compiler.h
@@ -54,7 +54,7 @@ class IRBuilder;
class JniCompiler {
public:
JniCompiler(LlvmCompilationUnit* cunit,
- CompilerDriver& driver,
+ CompilerDriver* driver,
const DexCompilationUnit* dex_compilation_unit);
CompiledMethod* Compile();
@@ -67,7 +67,7 @@ class JniCompiler {
private:
LlvmCompilationUnit* cunit_;
- CompilerDriver* driver_;
+ CompilerDriver* const driver_;
::llvm::Module* module_;
::llvm::LLVMContext* context_;
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 1417fb9e40..1c9aed83c3 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -24,7 +24,6 @@
#include "compiled_method.h"
#include "dex_file-inl.h"
#include "driver/compiler_driver.h"
-#include "disassembler.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "jni_internal.h"
#include "utils/assembler.h"
@@ -82,10 +81,8 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler,
UniquePtr<JniCallingConvention> end_jni_conv(
JniCallingConvention::Create(is_static, is_synchronized, jni_end_shorty, instruction_set));
-
// Assembler that holds generated instructions
UniquePtr<Assembler> jni_asm(Assembler::Create(instruction_set));
- bool should_disassemble = false;
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
@@ -356,9 +353,9 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler,
// 15. Process pending exceptions from JNI call or monitor exit.
__ ExceptionPoll(main_jni_conv->InterproceduralScratchRegister(), 0);
- // 16. Remove activation - no need to restore callee save registers because we didn't clobber
+ // 16. Remove activation - need to restore callee save registers since the GC may have changed
// them.
- __ RemoveFrame(frame_size, std::vector<ManagedRegister>());
+ __ RemoveFrame(frame_size, callee_save_regs);
// 17. Finalize code generation
__ EmitSlowPaths();
@@ -366,10 +363,6 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver& compiler,
std::vector<uint8_t> managed_code(cs);
MemoryRegion code(&managed_code[0], managed_code.size());
__ FinalizeInstructions(code);
- if (should_disassemble) {
- UniquePtr<Disassembler> disassembler(Disassembler::Create(instruction_set));
- disassembler->Dump(LOG(INFO), &managed_code[0], &managed_code[managed_code.size()]);
- }
return new CompiledMethod(compiler,
instruction_set,
managed_code,
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
index a917cdc6de..d59afd48b7 100644
--- a/compiler/llvm/compiler_llvm.cc
+++ b/compiler/llvm/compiler_llvm.cc
@@ -26,6 +26,7 @@
#include "ir_builder.h"
#include "jni/portable/jni_compiler.h"
#include "llvm_compilation_unit.h"
+#include "thread-inl.h"
#include "utils_llvm.h"
#include "verifier/method_verifier.h"
@@ -164,7 +165,7 @@ CompileNativeMethod(DexCompilationUnit* dex_compilation_unit) {
UniquePtr<LlvmCompilationUnit> cunit(AllocateCompilationUnit());
UniquePtr<JniCompiler> jni_compiler(
- new JniCompiler(cunit.get(), *compiler_driver_, dex_compilation_unit));
+ new JniCompiler(cunit.get(), compiler_driver_, dex_compilation_unit));
return jni_compiler->Compile();
}
diff --git a/compiler/llvm/gbc_expander.cc b/compiler/llvm/gbc_expander.cc
index 4f6fa0a2df..b206a25f25 100644
--- a/compiler/llvm/gbc_expander.cc
+++ b/compiler/llvm/gbc_expander.cc
@@ -846,10 +846,10 @@ llvm::Value* GBCExpanderPass::EmitInvoke(llvm::CallInst& call_inst) {
uintptr_t direct_code = 0;
uintptr_t direct_method = 0;
bool is_fast_path = driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_pc,
- invoke_type, target_method,
- vtable_idx,
- direct_code, direct_method,
- true);
+ true, true,
+ &invoke_type, &target_method,
+ &vtable_idx,
+ &direct_code, &direct_method);
// Load the method object
llvm::Value* callee_method_object_addr = NULL;
@@ -1630,7 +1630,7 @@ llvm::Value* GBCExpanderPass::Expand_HLIGet(llvm::CallInst& call_inst,
int field_offset;
bool is_volatile;
bool is_fast_path = driver_->ComputeInstanceFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, is_volatile, false);
+ field_idx, dex_compilation_unit_, false, &field_offset, &is_volatile);
if (!is_fast_path) {
llvm::Function* runtime_func;
@@ -1692,7 +1692,7 @@ void GBCExpanderPass::Expand_HLIPut(llvm::CallInst& call_inst,
int field_offset;
bool is_volatile;
bool is_fast_path = driver_->ComputeInstanceFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, is_volatile, true);
+ field_idx, dex_compilation_unit_, true, &field_offset, &is_volatile);
if (!is_fast_path) {
llvm::Function* runtime_func;
@@ -1897,8 +1897,8 @@ llvm::Value* GBCExpanderPass::Expand_HLSget(llvm::CallInst& call_inst,
bool is_volatile;
bool is_fast_path = driver_->ComputeStaticFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, ssb_index,
- is_referrers_class, is_volatile, false);
+ field_idx, dex_compilation_unit_, false,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
llvm::Value* static_field_value;
@@ -1981,8 +1981,8 @@ void GBCExpanderPass::Expand_HLSput(llvm::CallInst& call_inst,
bool is_volatile;
bool is_fast_path = driver_->ComputeStaticFieldInfo(
- field_idx, dex_compilation_unit_, field_offset, ssb_index,
- is_referrers_class, is_volatile, true);
+ field_idx, dex_compilation_unit_, true,
+ &field_offset, &ssb_index, &is_referrers_class, &is_volatile);
if (!is_fast_path) {
llvm::Function* runtime_func;
diff --git a/compiler/llvm/llvm_compilation_unit.cc b/compiler/llvm/llvm_compilation_unit.cc
index 139100bee9..feb495e35f 100644
--- a/compiler/llvm/llvm_compilation_unit.cc
+++ b/compiler/llvm/llvm_compilation_unit.cc
@@ -82,7 +82,6 @@
#include "ir_builder.h"
#include "os.h"
#include "runtime_support_builder_arm.h"
-#include "runtime_support_builder_thumb2.h"
#include "runtime_support_builder_x86.h"
#include "utils_llvm.h"
@@ -118,12 +117,10 @@ LlvmCompilationUnit::LlvmCompilationUnit(const CompilerLLVM* compiler_llvm, size
default:
runtime_support_.reset(new RuntimeSupportBuilder(*context_, *module_, *irb_));
break;
+ case kThumb2:
case kArm:
runtime_support_.reset(new RuntimeSupportBuilderARM(*context_, *module_, *irb_));
break;
- case kThumb2:
- runtime_support_.reset(new RuntimeSupportBuilderThumb2(*context_, *module_, *irb_));
- break;
case kX86:
runtime_support_.reset(new RuntimeSupportBuilderX86(*context_, *module_, *irb_));
break;
@@ -214,6 +211,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea
::llvm::TargetOptions target_options;
target_options.FloatABIType = ::llvm::FloatABI::Soft;
target_options.NoFramePointerElim = true;
+ target_options.NoFramePointerElimNonLeaf = true;
target_options.UseSoftFloat = false;
target_options.EnableFastISel = false;
@@ -257,7 +255,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea
::llvm::OwningPtr< ::llvm::tool_output_file> out_file(
new ::llvm::tool_output_file(bitcode_filename_.c_str(), errmsg,
- ::llvm::sys::fs::F_Binary));
+ ::llvm::raw_fd_ostream::F_Binary));
if (!errmsg.empty()) {
@@ -277,6 +275,7 @@ bool LlvmCompilationUnit::MaterializeToRawOStream(::llvm::raw_ostream& out_strea
// pm_builder.Inliner = ::llvm::createAlwaysInlinerPass();
// pm_builder.Inliner = ::llvm::createPartialInliningPass();
pm_builder.OptLevel = 3;
+ pm_builder.DisableSimplifyLibCalls = 1;
pm_builder.DisableUnitAtATime = 1;
pm_builder.populateFunctionPassManager(fpm);
pm_builder.populateModulePassManager(pm);
diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc
index 24e283d309..c825fbf190 100644
--- a/compiler/llvm/runtime_support_builder.cc
+++ b/compiler/llvm/runtime_support_builder.cc
@@ -164,89 +164,13 @@ void RuntimeSupportBuilder::EmitTestSuspend() {
/* Monitor */
void RuntimeSupportBuilder::EmitLockObject(::llvm::Value* object) {
- Value* monitor =
- irb_.LoadFromObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAARuntimeInfo);
-
- Value* real_monitor =
- irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
-
- // Is thin lock, unheld and not recursively acquired.
- Value* unheld = irb_.CreateICmpEQ(real_monitor, irb_.getInt32(0));
-
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* bb_fast = BasicBlock::Create(context_, "lock_fast", parent_func);
- BasicBlock* bb_slow = BasicBlock::Create(context_, "lock_slow", parent_func);
- BasicBlock* bb_cont = BasicBlock::Create(context_, "lock_cont", parent_func);
- irb_.CreateCondBr(unheld, bb_fast, bb_slow, kLikely);
-
- irb_.SetInsertPoint(bb_fast);
-
- // Calculate new monitor: new = old | (lock_id << LW_LOCK_OWNER_SHIFT)
- Value* lock_id =
- EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(),
- irb_.getInt32Ty(), kTBAARuntimeInfo);
-
- Value* owner = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT);
- Value* new_monitor = irb_.CreateOr(monitor, owner);
-
- // Atomically update monitor.
- Value* old_monitor =
- irb_.CompareExchangeObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- monitor, new_monitor, kTBAARuntimeInfo);
-
- Value* retry_slow_path = irb_.CreateICmpEQ(old_monitor, monitor);
- irb_.CreateCondBr(retry_slow_path, bb_cont, bb_slow, kLikely);
-
- irb_.SetInsertPoint(bb_slow);
Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_cont);
}
void RuntimeSupportBuilder::EmitUnlockObject(::llvm::Value* object) {
- Value* lock_id =
- EmitLoadFromThreadOffset(Thread::ThinLockIdOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAARuntimeInfo);
- Value* monitor =
- irb_.LoadFromObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- irb_.getJIntTy(),
- kTBAARuntimeInfo);
-
- Value* my_monitor = irb_.CreateShl(lock_id, LW_LOCK_OWNER_SHIFT);
- Value* hash_state = irb_.CreateAnd(monitor, (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
- Value* real_monitor = irb_.CreateAnd(monitor, ~(LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT));
-
- // Is thin lock, held by us and not recursively acquired
- Value* is_fast_path = irb_.CreateICmpEQ(real_monitor, my_monitor);
-
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* bb_fast = BasicBlock::Create(context_, "unlock_fast", parent_func);
- BasicBlock* bb_slow = BasicBlock::Create(context_, "unlock_slow", parent_func);
- BasicBlock* bb_cont = BasicBlock::Create(context_, "unlock_cont", parent_func);
- irb_.CreateCondBr(is_fast_path, bb_fast, bb_slow, kLikely);
-
- irb_.SetInsertPoint(bb_fast);
- // Set all bits to zero (except hash state)
- irb_.StoreToObjectOffset(object,
- mirror::Object::MonitorOffset().Int32Value(),
- hash_state,
- kTBAARuntimeInfo);
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_slow);
Function* slow_func = GetRuntimeSupportFunction(runtime_support::UnlockObject);
irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
- irb_.CreateBr(bb_cont);
-
- irb_.SetInsertPoint(bb_cont);
}
diff --git a/compiler/llvm/runtime_support_builder.h b/compiler/llvm/runtime_support_builder.h
index e92ac0a908..898611af75 100644
--- a/compiler/llvm/runtime_support_builder.h
+++ b/compiler/llvm/runtime_support_builder.h
@@ -64,8 +64,8 @@ class RuntimeSupportBuilder {
virtual void EmitTestSuspend();
/* Monitor */
- virtual void EmitLockObject(::llvm::Value* object);
- virtual void EmitUnlockObject(::llvm::Value* object);
+ void EmitLockObject(::llvm::Value* object);
+ void EmitUnlockObject(::llvm::Value* object);
/* MarkGCCard */
virtual void EmitMarkGCCard(::llvm::Value* value, ::llvm::Value* target_addr);
diff --git a/compiler/llvm/runtime_support_builder_arm.cc b/compiler/llvm/runtime_support_builder_arm.cc
index 569d825272..cad46247fd 100644
--- a/compiler/llvm/runtime_support_builder_arm.cc
+++ b/compiler/llvm/runtime_support_builder_arm.cc
@@ -116,24 +116,5 @@ Value* RuntimeSupportBuilderARM::EmitSetCurrentThread(Value* thread) {
return old_thread_register;
}
-
-/* Monitor */
-
-void RuntimeSupportBuilderARM::EmitLockObject(Value* object) {
- RuntimeSupportBuilder::EmitLockObject(object);
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*isVarArg=*/false);
- InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true);
- irb_.CreateCall(func);
-}
-
-void RuntimeSupportBuilderARM::EmitUnlockObject(Value* object) {
- RuntimeSupportBuilder::EmitUnlockObject(object);
- FunctionType* func_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*isVarArg=*/false);
- InlineAsm* func = InlineAsm::get(func_ty, "dmb sy", "", true);
- irb_.CreateCall(func);
-}
-
} // namespace llvm
} // namespace art
diff --git a/compiler/llvm/runtime_support_builder_arm.h b/compiler/llvm/runtime_support_builder_arm.h
index 5a353d7f30..0d01509be0 100644
--- a/compiler/llvm/runtime_support_builder_arm.h
+++ b/compiler/llvm/runtime_support_builder_arm.h
@@ -34,10 +34,6 @@ class RuntimeSupportBuilderARM : public RuntimeSupportBuilder {
virtual void EmitStoreToThreadOffset(int64_t offset, ::llvm::Value* value,
TBAASpecialType s_ty);
virtual ::llvm::Value* EmitSetCurrentThread(::llvm::Value* thread);
-
- /* Monitor */
- virtual void EmitLockObject(::llvm::Value* object);
- virtual void EmitUnlockObject(::llvm::Value* object);
};
} // namespace llvm
diff --git a/compiler/llvm/runtime_support_builder_thumb2.cc b/compiler/llvm/runtime_support_builder_thumb2.cc
deleted file mode 100644
index eff29c8b04..0000000000
--- a/compiler/llvm/runtime_support_builder_thumb2.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2012 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 "runtime_support_builder_thumb2.h"
-
-#include "ir_builder.h"
-#include "mirror/object.h"
-#include "monitor.h"
-#include "thread.h"
-#include "utils_llvm.h"
-
-#include <llvm/IR/DerivedTypes.h>
-#include <llvm/IR/Function.h>
-#include <llvm/IR/InlineAsm.h>
-#include <llvm/IR/Module.h>
-#include <llvm/IR/Type.h>
-
-#include <inttypes.h>
-#include <vector>
-
-using ::llvm::BasicBlock;
-using ::llvm::Function;
-using ::llvm::FunctionType;
-using ::llvm::InlineAsm;
-using ::llvm::Type;
-using ::llvm::Value;
-
-namespace art {
-namespace llvm {
-
-
-void RuntimeSupportBuilderThumb2::EmitLockObject(Value* object) {
- FunctionType* func_ty = FunctionType::get(/*Result=*/irb_.getInt32Ty(),
- /*Params=*/irb_.getJObjectTy(),
- /*isVarArg=*/false);
- // $0: result
- // $1: object
- // $2: temp
- // $3: temp
- std::string asms;
- StringAppendF(&asms, "add $3, $1, #%" PRId32 "\n", mirror::Object::MonitorOffset().Int32Value());
- StringAppendF(&asms, "ldr $2, [r9, #%" PRId32 "]\n", Thread::ThinLockIdOffset().Int32Value());
- StringAppendF(&asms, "ldrex $0, [$3]\n");
- StringAppendF(&asms, "lsl $2, $2, %d\n", LW_LOCK_OWNER_SHIFT);
- StringAppendF(&asms, "bfi $2, $0, #0, #%d\n", LW_LOCK_OWNER_SHIFT - 1);
- StringAppendF(&asms, "bfc $0, #%d, #%d\n", LW_HASH_STATE_SHIFT, LW_LOCK_OWNER_SHIFT - 1);
- StringAppendF(&asms, "cmp $0, #0\n");
- StringAppendF(&asms, "it eq\n");
- StringAppendF(&asms, "strexeq $0, $2, [$3]\n");
-
- InlineAsm* func = InlineAsm::get(func_ty, asms, "=&l,l,~l,~l", true);
-
- Value* retry_slow_path = irb_.CreateCall(func, object);
- retry_slow_path = irb_.CreateICmpNE(retry_slow_path, irb_.getJInt(0));
-
- Function* parent_func = irb_.GetInsertBlock()->getParent();
- BasicBlock* basic_block_lock = BasicBlock::Create(context_, "lock", parent_func);
- BasicBlock* basic_block_cont = BasicBlock::Create(context_, "lock_cont", parent_func);
- irb_.CreateCondBr(retry_slow_path, basic_block_lock, basic_block_cont, kUnlikely);
-
- irb_.SetInsertPoint(basic_block_lock);
- Function* slow_func = GetRuntimeSupportFunction(runtime_support::LockObject);
- irb_.CreateCall2(slow_func, object, EmitGetCurrentThread());
- irb_.CreateBr(basic_block_cont);
-
- irb_.SetInsertPoint(basic_block_cont);
- { // Memory barrier
- FunctionType* asm_ty = FunctionType::get(/*Result=*/Type::getVoidTy(context_),
- /*isVarArg=*/false);
- InlineAsm* func = InlineAsm::get(asm_ty, "dmb sy", "", true);
- irb_.CreateCall(func);
- }
-}
-
-
-} // namespace llvm
-} // namespace art
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index bfba9c0c0c..6213b45c41 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -28,6 +28,8 @@ namespace art {
class OatTest : public CommonTest {
protected:
+ static const bool kCompile = false; // DISABLED_ due to the time to compile libcore
+
void CheckMethod(mirror::ArtMethod* method,
const OatFile::OatMethod& oat_method,
const DexFile* dex_file)
@@ -40,7 +42,7 @@ class OatTest : public CommonTest {
EXPECT_TRUE(oat_method.GetCode() == NULL) << PrettyMethod(method) << " "
<< oat_method.GetCode();
#if !defined(ART_USE_PORTABLE_COMPILER)
- EXPECT_EQ(oat_method.GetFrameSizeInBytes(), static_cast<uint32_t>(kStackAlignment));
+ EXPECT_EQ(oat_method.GetFrameSizeInBytes(), kCompile ? kStackAlignment : 0);
EXPECT_EQ(oat_method.GetCoreSpillMask(), 0U);
EXPECT_EQ(oat_method.GetFpSpillMask(), 0U);
#endif
@@ -65,7 +67,6 @@ class OatTest : public CommonTest {
};
TEST_F(OatTest, WriteRead) {
- const bool compile = false; // DISABLED_ due to the time to compile libcore
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// TODO: make selectable
@@ -75,9 +76,12 @@ TEST_F(OatTest, WriteRead) {
CompilerBackend compiler_backend = kQuick;
#endif
InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
- compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set, false, NULL, 2, true));
+
+ InstructionSetFeatures insn_features;
+ compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set,
+ insn_features, false, NULL, 2, true));
jobject class_loader = NULL;
- if (compile) {
+ if (kCompile) {
base::TimingLogger timings("OatTest::WriteRead", false, false);
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
}
@@ -96,37 +100,42 @@ TEST_F(OatTest, WriteRead) {
tmp.GetFile());
ASSERT_TRUE(success);
- if (compile) { // OatWriter strips the code, regenerate to compare
+ if (kCompile) { // OatWriter strips the code, regenerate to compare
base::TimingLogger timings("CommonTest::WriteRead", false, false);
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), timings);
}
- UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false));
- ASSERT_TRUE(oat_file.get() != NULL);
+ std::string error_msg;
+ UniquePtr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), NULL, false,
+ &error_msg));
+ ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
- ASSERT_EQ(2U, oat_header.GetDexFileCount()); // core and conscrypt
+ ASSERT_EQ(1U, oat_header.GetDexFileCount()); // core
ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
ASSERT_EQ(4096U, oat_header.GetImageFileLocationOatDataBegin());
ASSERT_EQ("lue.art", oat_header.GetImageFileLocation());
const DexFile* dex_file = java_lang_dex_file_;
uint32_t dex_file_checksum = dex_file->GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation(),
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file->GetLocation().c_str(),
&dex_file_checksum);
+ ASSERT_TRUE(oat_dex_file != nullptr);
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
for (size_t i = 0; i < dex_file->NumClassDefs(); i++) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
const byte* class_data = dex_file->GetClassData(class_def);
- size_t num_virtual_methods =0;
+ size_t num_virtual_methods = 0;
if (class_data != NULL) {
ClassDataItemIterator it(*dex_file, class_data);
num_virtual_methods = it.NumVirtualMethods();
}
const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ mirror::Class* klass = class_linker->FindClass(descriptor, NULL);
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i));
-
- mirror::Class* klass = class_linker->FindClass(descriptor, NULL);
+ CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor;
+ CHECK_EQ(kCompile ? OatClassType::kOatClassAllCompiled : OatClassType::kOatClassNoneCompiled,
+ oat_class->GetType()) << descriptor;
size_t method_index = 0;
for (size_t i = 0; i < klass->NumDirectMethods(); i++, method_index++) {
@@ -143,17 +152,19 @@ TEST_F(OatTest, WriteRead) {
TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
- EXPECT_EQ(64U, sizeof(OatHeader));
+ EXPECT_EQ(76U, sizeof(OatHeader));
EXPECT_EQ(28U, sizeof(OatMethodOffsets));
}
TEST_F(OatTest, OatHeaderIsValid) {
InstructionSet instruction_set = kX86;
+ InstructionSetFeatures instruction_set_features;
std::vector<const DexFile*> dex_files;
uint32_t image_file_location_oat_checksum = 0;
uint32_t image_file_location_oat_begin = 0;
const std::string image_file_location;
OatHeader oat_header(instruction_set,
+ instruction_set_features,
&dex_files,
image_file_location_oat_checksum,
image_file_location_oat_begin,
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index f9d6e4192d..f3bb11272e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -18,6 +18,7 @@
#include <zlib.h>
+#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
@@ -54,8 +55,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
size_interpreter_to_interpreter_bridge_(0),
size_interpreter_to_compiled_code_bridge_(0),
size_jni_dlsym_lookup_(0),
+ size_portable_imt_conflict_trampoline_(0),
size_portable_resolution_trampoline_(0),
size_portable_to_interpreter_bridge_(0),
+ size_quick_imt_conflict_trampoline_(0),
size_quick_resolution_trampoline_(0),
size_quick_to_interpreter_bridge_(0),
size_trampoline_alignment_(0),
@@ -70,7 +73,9 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
size_oat_dex_file_location_checksum_(0),
size_oat_dex_file_offset_(0),
size_oat_dex_file_methods_offsets_(0),
+ size_oat_class_type_(0),
size_oat_class_status_(0),
+ size_oat_class_method_bitmaps_(0),
size_oat_class_method_offsets_(0) {
size_t offset = InitOatHeader();
offset = InitOatDexFiles(offset);
@@ -93,6 +98,7 @@ OatWriter::~OatWriter() {
size_t OatWriter::InitOatHeader() {
// create the OatHeader
oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(),
+ compiler_driver_->GetInstructionSetFeatures(),
dex_files_,
image_file_location_oat_checksum_,
image_file_location_oat_begin_,
@@ -142,12 +148,48 @@ size_t OatWriter::InitOatClasses(size_t offset) {
oat_dex_files_[i]->methods_offsets_[class_def_index] = offset;
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
const byte* class_data = dex_file->GetClassData(class_def);
- uint32_t num_methods = 0;
+ uint32_t num_non_null_compiled_methods = 0;
+ UniquePtr<std::vector<CompiledMethod*> > compiled_methods(new std::vector<CompiledMethod*>());
if (class_data != NULL) { // ie not an empty class, such as a marker interface
ClassDataItemIterator it(*dex_file, class_data);
size_t num_direct_methods = it.NumDirectMethods();
size_t num_virtual_methods = it.NumVirtualMethods();
- num_methods = num_direct_methods + num_virtual_methods;
+ size_t num_methods = num_direct_methods + num_virtual_methods;
+
+ // 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
+ // OatMethodOffsets for the compiled methods.
+ compiled_methods->reserve(num_methods);
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+ size_t class_def_method_index = 0;
+ while (it.HasNextDirectMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
+ compiled_methods->push_back(compiled_method);
+ if (compiled_method != NULL) {
+ num_non_null_compiled_methods++;
+ }
+ class_def_method_index++;
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ CompiledMethod* compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
+ compiled_methods->push_back(compiled_method);
+ if (compiled_method != NULL) {
+ num_non_null_compiled_methods++;
+ }
+ class_def_method_index++;
+ it.Next();
+ }
}
ClassReference class_ref(dex_file, class_def_index);
@@ -161,7 +203,8 @@ size_t OatWriter::InitOatClasses(size_t offset) {
status = mirror::Class::kStatusNotReady;
}
- OatClass* oat_class = new OatClass(offset, status, num_methods);
+ OatClass* oat_class = new OatClass(offset, compiled_methods.release(),
+ num_non_null_compiled_methods, status);
oat_classes_.push_back(oat_class);
offset += oat_class->SizeOf();
}
@@ -189,8 +232,10 @@ size_t OatWriter::InitOatCode(size_t offset) {
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_, InterpreterToInterpreterBridge);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_, InterpreterToCompiledCodeBridge);
DO_TRAMPOLINE(jni_dlsym_lookup_, JniDlsymLookup);
+ DO_TRAMPOLINE(portable_imt_conflict_trampoline_, PortableImtConflictTrampoline);
DO_TRAMPOLINE(portable_resolution_trampoline_, PortableResolutionTrampoline);
DO_TRAMPOLINE(portable_to_interpreter_bridge_, PortableToInterpreterBridge);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_, QuickImtConflictTrampoline);
DO_TRAMPOLINE(quick_resolution_trampoline_, QuickResolutionTrampoline);
DO_TRAMPOLINE(quick_to_interpreter_bridge_, QuickToInterpreterBridge);
@@ -199,8 +244,10 @@ size_t OatWriter::InitOatCode(size_t offset) {
oat_header_->SetInterpreterToInterpreterBridgeOffset(0);
oat_header_->SetInterpreterToCompiledCodeBridgeOffset(0);
oat_header_->SetJniDlsymLookupOffset(0);
+ oat_header_->SetPortableImtConflictTrampolineOffset(0);
oat_header_->SetPortableResolutionTrampolineOffset(0);
oat_header_->SetPortableToInterpreterBridgeOffset(0);
+ oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
oat_header_->SetQuickToInterpreterBridgeOffset(0);
}
@@ -212,20 +259,20 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
for (size_t i = 0; i != dex_files_->size(); ++i) {
const DexFile* dex_file = (*dex_files_)[i];
CHECK(dex_file != NULL);
- offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file);
+ offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file);
}
return offset;
}
size_t OatWriter::InitOatCodeDexFile(size_t offset,
- size_t& oat_class_index,
+ size_t* oat_class_index,
const DexFile& dex_file) {
for (size_t class_def_index = 0;
class_def_index < dex_file.NumClassDefs();
- class_def_index++, oat_class_index++) {
+ class_def_index++, (*oat_class_index)++) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- offset = InitOatCodeClassDef(offset, oat_class_index, class_def_index, dex_file, class_def);
- oat_classes_[oat_class_index]->UpdateChecksum(*oat_header_);
+ offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def);
+ oat_classes_[*oat_class_index]->UpdateChecksum(*oat_header_);
}
return offset;
}
@@ -240,7 +287,7 @@ size_t OatWriter::InitOatCodeClassDef(size_t offset,
return offset;
}
ClassDataItemIterator it(dex_file, class_data);
- CHECK_EQ(oat_classes_[oat_class_index]->method_offsets_.size(),
+ CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(),
it.NumDirectMethods() + it.NumVirtualMethods());
// Skip fields
while (it.HasNextStaticField()) {
@@ -251,32 +298,35 @@ size_t OatWriter::InitOatCodeClassDef(size_t offset,
}
// Process methods
size_t class_def_method_index = 0;
+ size_t method_offsets_index = 0;
while (it.HasNextDirectMethod()) {
bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
- is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(),
- &dex_file);
+ &method_offsets_index, is_native,
+ it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
class_def_method_index++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
- is_native, it.GetMethodInvokeType(class_def), it.GetMemberIndex(),
- &dex_file);
+ &method_offsets_index, is_native,
+ it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
class_def_method_index++;
it.Next();
}
DCHECK(!it.HasNext());
+ CHECK_LE(method_offsets_index, class_def_method_index);
return offset;
}
size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
size_t __attribute__((unused)) class_def_index,
size_t class_def_method_index,
+ size_t* method_offsets_index,
bool __attribute__((unused)) is_native,
InvokeType invoke_type,
- uint32_t method_idx, const DexFile* dex_file) {
+ uint32_t method_idx, const DexFile& dex_file) {
// derived from CompiledMethod if available
uint32_t code_offset = 0;
uint32_t frame_size_in_bytes = kStackAlignment;
@@ -292,8 +342,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
#endif
- CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
+ CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != NULL) {
#if defined(ART_USE_PORTABLE_COMPILER)
compiled_method->AddOatdataOffsetToCompliledCodeOffset(
@@ -358,7 +407,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
#if !defined(NDEBUG)
// We expect GC maps except when the class hasn't been verified or the method is native
- ClassReference class_ref(dex_file, class_def_index);
+ ClassReference class_ref(&dex_file, class_def_index);
CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref);
mirror::Class::Status status;
if (compiled_class != NULL) {
@@ -371,7 +420,7 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
<< &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
<< (status < mirror::Class::kStatusVerified) << " " << status << " "
- << PrettyMethod(method_idx, *dex_file);
+ << PrettyMethod(method_idx, dex_file);
#endif
// Deduplicate GC maps
@@ -384,24 +433,26 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
offset += gc_map_size;
oat_header_->UpdateChecksum(&gc_map[0], gc_map_size);
}
+
+ oat_class->method_offsets_[*method_offsets_index] =
+ OatMethodOffsets(code_offset,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ mapping_table_offset,
+ vmap_table_offset,
+ gc_map_offset);
+ (*method_offsets_index)++;
}
- oat_class->method_offsets_[class_def_method_index] =
- OatMethodOffsets(code_offset,
- frame_size_in_bytes,
- core_spill_mask,
- fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
- gc_map_offset);
if (compiler_driver_->IsImage()) {
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file);
+ mirror::DexCache* dex_cache = linker->FindDexCache(dex_file);
// Unchecked as we hold mutator_lock_ on entry.
ScopedObjectAccessUnchecked soa(Thread::Current());
- mirror::ArtMethod* method = linker->ResolveMethod(*dex_file, method_idx, dex_cache,
- NULL, NULL, invoke_type);
+ mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache,
+ NULL, NULL, invoke_type);
CHECK(method != NULL);
method->SetFrameSizeInBytes(frame_size_in_bytes);
method->SetCoreSpillMask(core_spill_mask);
@@ -475,8 +526,10 @@ bool OatWriter::Write(OutputStream& out) {
DO_STAT(size_interpreter_to_interpreter_bridge_);
DO_STAT(size_interpreter_to_compiled_code_bridge_);
DO_STAT(size_jni_dlsym_lookup_);
+ DO_STAT(size_portable_imt_conflict_trampoline_);
DO_STAT(size_portable_resolution_trampoline_);
DO_STAT(size_portable_to_interpreter_bridge_);
+ DO_STAT(size_quick_imt_conflict_trampoline_);
DO_STAT(size_quick_resolution_trampoline_);
DO_STAT(size_quick_to_interpreter_bridge_);
DO_STAT(size_trampoline_alignment_);
@@ -491,7 +544,9 @@ bool OatWriter::Write(OutputStream& out) {
DO_STAT(size_oat_dex_file_location_checksum_);
DO_STAT(size_oat_dex_file_offset_);
DO_STAT(size_oat_dex_file_methods_offsets_);
+ DO_STAT(size_oat_class_type_);
DO_STAT(size_oat_class_status_);
+ DO_STAT(size_oat_class_method_bitmaps_);
DO_STAT(size_oat_class_method_offsets_);
#undef DO_STAT
@@ -570,8 +625,10 @@ size_t OatWriter::WriteCode(OutputStream& out, const size_t file_offset) {
DO_TRAMPOLINE(interpreter_to_interpreter_bridge_);
DO_TRAMPOLINE(interpreter_to_compiled_code_bridge_);
DO_TRAMPOLINE(jni_dlsym_lookup_);
+ DO_TRAMPOLINE(portable_imt_conflict_trampoline_);
DO_TRAMPOLINE(portable_resolution_trampoline_);
DO_TRAMPOLINE(portable_to_interpreter_bridge_);
+ DO_TRAMPOLINE(quick_imt_conflict_trampoline_);
DO_TRAMPOLINE(quick_resolution_trampoline_);
DO_TRAMPOLINE(quick_to_interpreter_bridge_);
#undef DO_TRAMPOLINE
@@ -586,7 +643,7 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream& out,
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
const DexFile* dex_file = (*dex_files_)[i];
CHECK(dex_file != NULL);
- relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, oat_class_index,
+ relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index,
*dex_file);
if (relative_offset == 0) {
return 0;
@@ -596,12 +653,12 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream& out,
}
size_t OatWriter::WriteCodeDexFile(OutputStream& out, const size_t file_offset,
- size_t relative_offset, size_t& oat_class_index,
+ size_t relative_offset, size_t* oat_class_index,
const DexFile& dex_file) {
for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs();
- class_def_index++, oat_class_index++) {
+ class_def_index++, (*oat_class_index)++) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, oat_class_index,
+ relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index,
dex_file, class_def);
if (relative_offset == 0) {
return 0;
@@ -637,11 +694,12 @@ size_t OatWriter::WriteCodeClassDef(OutputStream& out,
}
// Process methods
size_t class_def_method_index = 0;
+ size_t method_offsets_index = 0;
while (it.HasNextDirectMethod()) {
bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0;
relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
- class_def_method_index, is_static, it.GetMemberIndex(),
- dex_file);
+ class_def_method_index, &method_offsets_index, is_static,
+ it.GetMemberIndex(), dex_file);
if (relative_offset == 0) {
return 0;
}
@@ -650,28 +708,30 @@ size_t OatWriter::WriteCodeClassDef(OutputStream& out,
}
while (it.HasNextVirtualMethod()) {
relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
- class_def_method_index, false, it.GetMemberIndex(), dex_file);
+ class_def_method_index, &method_offsets_index, false,
+ it.GetMemberIndex(), dex_file);
if (relative_offset == 0) {
return 0;
}
class_def_method_index++;
it.Next();
}
+ DCHECK(!it.HasNext());
+ CHECK_LE(method_offsets_index, class_def_method_index);
return relative_offset;
}
size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset,
size_t relative_offset, size_t oat_class_index,
- size_t class_def_method_index, bool is_static,
- uint32_t method_idx, const DexFile& dex_file) {
- const CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(&dex_file, method_idx));
-
- OatMethodOffsets method_offsets =
- oat_classes_[oat_class_index]->method_offsets_[class_def_method_index];
-
+ size_t class_def_method_index, size_t* method_offsets_index,
+ bool is_static, uint32_t method_idx, const DexFile& dex_file) {
+ OatClass* oat_class = oat_classes_[oat_class_index];
+ const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
if (compiled_method != NULL) { // ie. not an abstract method
+ const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index];
+ (*method_offsets_index)++;
+
#if !defined(ART_USE_PORTABLE_COMPILER)
uint32_t aligned_offset = compiled_method->AlignCode(relative_offset);
uint32_t aligned_code_delta = aligned_offset - relative_offset;
@@ -854,29 +914,96 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer,
return true;
}
-OatWriter::OatClass::OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count) {
+OatWriter::OatClass::OatClass(size_t offset,
+ std::vector<CompiledMethod*>* compiled_methods,
+ uint32_t num_non_null_compiled_methods,
+ mirror::Class::Status status) {
+ CHECK(compiled_methods != NULL);
+ uint32_t num_methods = compiled_methods->size();
+ CHECK_LE(num_non_null_compiled_methods, num_methods);
+
offset_ = offset;
+ compiled_methods_ = compiled_methods;
+ oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
+
+ // Since both kOatClassNoneCompiled and kOatClassAllCompiled could
+ // apply when there are 0 methods, we just arbitrarily say that 0
+ // methods means kOatClassNoneCompiled and that we won't use
+ // kOatClassAllCompiled unless there is at least one compiled
+ // method. This means in an interpretter only system, we can assert
+ // that all classes are kOatClassNoneCompiled.
+ if (num_non_null_compiled_methods == 0) {
+ type_ = kOatClassNoneCompiled;
+ } else if (num_non_null_compiled_methods == num_methods) {
+ type_ = kOatClassAllCompiled;
+ } else {
+ type_ = kOatClassSomeCompiled;
+ }
+
status_ = status;
- method_offsets_.resize(methods_count);
+ method_offsets_.resize(num_non_null_compiled_methods);
+
+ uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
+ if (type_ == kOatClassSomeCompiled) {
+ method_bitmap_ = new BitVector(num_methods, false, Allocator::GetMallocAllocator());
+ method_bitmap_size_ = method_bitmap_->GetSizeOf();
+ oat_method_offsets_offset_from_oat_class += sizeof(method_bitmap_size_);
+ oat_method_offsets_offset_from_oat_class += method_bitmap_size_;
+ } else {
+ method_bitmap_ = NULL;
+ method_bitmap_size_ = 0;
+ }
+
+ for (size_t i = 0; i < num_methods; i++) {
+ CompiledMethod* compiled_method = (*compiled_methods_)[i];
+ if (compiled_method == NULL) {
+ oat_method_offsets_offsets_from_oat_class_[i] = 0;
+ } else {
+ oat_method_offsets_offsets_from_oat_class_[i] = oat_method_offsets_offset_from_oat_class;
+ oat_method_offsets_offset_from_oat_class += sizeof(OatMethodOffsets);
+ if (type_ == kOatClassSomeCompiled) {
+ method_bitmap_->SetBit(i);
+ }
+ }
+ }
}
+OatWriter::OatClass::~OatClass() {
+ delete compiled_methods_;
+}
+
+#if defined(ART_USE_PORTABLE_COMPILER)
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(
size_t class_def_method_index_) const {
- return offset_ + GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
+ uint32_t method_offset = GetOatMethodOffsetsOffsetFromOatClass(class_def_method_index_);
+ if (method_offset == 0) {
+ return 0;
+ }
+ return offset_ + method_offset;
}
size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatClass(
size_t class_def_method_index_) const {
- return sizeof(status_)
- + (sizeof(method_offsets_[0]) * class_def_method_index_);
+ return oat_method_offsets_offsets_from_oat_class_[class_def_method_index_];
}
+#endif
size_t OatWriter::OatClass::SizeOf() const {
- return GetOatMethodOffsetsOffsetFromOatClass(method_offsets_.size());
+ return sizeof(status_)
+ + sizeof(type_)
+ + ((method_bitmap_size_ == 0) ? 0 : sizeof(method_bitmap_size_))
+ + method_bitmap_size_
+ + (sizeof(method_offsets_[0]) * method_offsets_.size());
}
void OatWriter::OatClass::UpdateChecksum(OatHeader& oat_header) const {
oat_header.UpdateChecksum(&status_, sizeof(status_));
+ oat_header.UpdateChecksum(&type_, sizeof(type_));
+ if (method_bitmap_size_ != 0) {
+ CHECK_EQ(kOatClassSomeCompiled, type_);
+ oat_header.UpdateChecksum(&method_bitmap_size_, sizeof(method_bitmap_size_));
+ oat_header.UpdateChecksum(method_bitmap_->GetRawStorage(), method_bitmap_size_);
+ }
oat_header.UpdateChecksum(&method_offsets_[0],
sizeof(method_offsets_[0]) * method_offsets_.size());
}
@@ -890,17 +1017,30 @@ bool OatWriter::OatClass::Write(OatWriter* oat_writer,
return false;
}
oat_writer->size_oat_class_status_ += sizeof(status_);
- DCHECK_EQ(static_cast<off_t>(file_offset + GetOatMethodOffsetsOffsetFromOatHeader(0)),
- out.Seek(0, kSeekCurrent));
+ if (!out.WriteFully(&type_, sizeof(type_))) {
+ PLOG(ERROR) << "Failed to write oat class type to " << out.GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_type_ += sizeof(type_);
+ if (method_bitmap_size_ != 0) {
+ CHECK_EQ(kOatClassSomeCompiled, type_);
+ if (!out.WriteFully(&method_bitmap_size_, sizeof(method_bitmap_size_))) {
+ PLOG(ERROR) << "Failed to write method bitmap size to " << out.GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_bitmaps_ += sizeof(method_bitmap_size_);
+ if (!out.WriteFully(method_bitmap_->GetRawStorage(), method_bitmap_size_)) {
+ PLOG(ERROR) << "Failed to write method bitmap to " << out.GetLocation();
+ return false;
+ }
+ oat_writer->size_oat_class_method_bitmaps_ += method_bitmap_size_;
+ }
if (!out.WriteFully(&method_offsets_[0],
sizeof(method_offsets_[0]) * method_offsets_.size())) {
PLOG(ERROR) << "Failed to write method offsets to " << out.GetLocation();
return false;
}
oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
- DCHECK_EQ(static_cast<off_t>(file_offset +
- GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())),
- out.Seek(0, kSeekCurrent));
return true;
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index d5f7e21a1a..5d947cfaea 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -30,6 +30,7 @@
namespace art {
+class BitVector;
class OutputStream;
// OatHeader variable length with count of D OatDexFiles
@@ -90,7 +91,7 @@ class OatWriter {
size_t InitOatCodeDexFiles(size_t offset)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeDexFile(size_t offset,
- size_t& oat_class_index,
+ size_t* oat_class_index,
const DexFile& dex_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeClassDef(size_t offset,
@@ -99,21 +100,22 @@ class OatWriter {
const DexFile::ClassDef& class_def)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index,
- size_t class_def_method_index, bool is_native, InvokeType type,
- uint32_t method_idx, const DexFile*)
+ size_t class_def_method_index, size_t* method_offsets_index,
+ bool is_native, InvokeType type, uint32_t method_idx, const DexFile&)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool WriteTables(OutputStream& out, const size_t file_offset);
size_t WriteCode(OutputStream& out, const size_t file_offset);
size_t WriteCodeDexFiles(OutputStream& out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFile(OutputStream& out, const size_t file_offset, size_t relative_offset,
- size_t& oat_class_index, const DexFile& dex_file);
+ size_t* oat_class_index, const DexFile& dex_file);
size_t WriteCodeClassDef(OutputStream& out, const size_t file_offset, size_t relative_offset,
size_t oat_class_index, const DexFile& dex_file,
const DexFile::ClassDef& class_def);
size_t WriteCodeMethod(OutputStream& out, const size_t file_offset, size_t relative_offset,
- size_t oat_class_index, size_t class_def_method_index, bool is_static,
- uint32_t method_idx, const DexFile& dex_file);
+ size_t oat_class_index, size_t class_def_method_index,
+ size_t* method_offsets_index, bool is_static, uint32_t method_idx,
+ const DexFile& dex_file);
void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file,
OutputStream& out) const;
@@ -142,13 +144,24 @@ class OatWriter {
class OatClass {
public:
- explicit OatClass(size_t offset, mirror::Class::Status status, uint32_t methods_count);
+ explicit OatClass(size_t offset,
+ std::vector<CompiledMethod*>* compiled_methods,
+ uint32_t num_non_null_compiled_methods,
+ mirror::Class::Status status);
+ ~OatClass();
+#if defined(ART_USE_PORTABLE_COMPILER)
size_t GetOatMethodOffsetsOffsetFromOatHeader(size_t class_def_method_index_) const;
size_t GetOatMethodOffsetsOffsetFromOatClass(size_t class_def_method_index_) const;
+#endif
size_t SizeOf() const;
void UpdateChecksum(OatHeader& oat_header) const;
bool Write(OatWriter* oat_writer, OutputStream& out, const size_t file_offset) const;
+ CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
+ DCHECK(compiled_methods_ != NULL);
+ return (*compiled_methods_)[class_def_method_index];
+ }
+
// Offset of start of OatClass from beginning of OatHeader. It is
// used to validate file position when writing. For Portable, it
// is also used to calculate the position of the OatMethodOffsets
@@ -156,8 +169,37 @@ class OatWriter {
// patched to point to code in the Portable .o ELF objects.
size_t offset_;
+ // CompiledMethods for each class_def_method_index, or NULL if no method is available.
+ std::vector<CompiledMethod*>* compiled_methods_;
+
+ // Offset from OatClass::offset_ to the OatMethodOffsets for the
+ // class_def_method_index. If 0, it means the corresponding
+ // CompiledMethod entry in OatClass::compiled_methods_ should be
+ // NULL and that the OatClass::type_ should be kOatClassBitmap.
+ std::vector<uint32_t> oat_method_offsets_offsets_from_oat_class_;
+
// data to write
- mirror::Class::Status status_;
+
+ COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits);
+ int16_t status_;
+
+ COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits);
+ uint16_t type_;
+
+ uint32_t method_bitmap_size_;
+
+ // bit vector indexed by ClassDef method index. When
+ // OatClassType::type_ is kOatClassBitmap, a set bit indicates the
+ // method has an OatMethodOffsets in methods_offsets_, otherwise
+ // the entry was ommited to save space. If OatClassType::type_ is
+ // not is kOatClassBitmap, the bitmap will be NULL.
+ BitVector* method_bitmap_;
+
+ // OatMethodOffsets for each CompiledMethod present in the
+ // OatClass. Note that some may be missing if
+ // OatClass::compiled_methods_ contains NULL values (and
+ // oat_method_offsets_offsets_from_oat_class_ should contain 0
+ // values in this case).
std::vector<OatMethodOffsets> method_offsets_;
private:
@@ -184,8 +226,10 @@ class OatWriter {
UniquePtr<const std::vector<uint8_t> > interpreter_to_interpreter_bridge_;
UniquePtr<const std::vector<uint8_t> > interpreter_to_compiled_code_bridge_;
UniquePtr<const std::vector<uint8_t> > jni_dlsym_lookup_;
+ UniquePtr<const std::vector<uint8_t> > portable_imt_conflict_trampoline_;
UniquePtr<const std::vector<uint8_t> > portable_resolution_trampoline_;
UniquePtr<const std::vector<uint8_t> > portable_to_interpreter_bridge_;
+ UniquePtr<const std::vector<uint8_t> > quick_imt_conflict_trampoline_;
UniquePtr<const std::vector<uint8_t> > quick_resolution_trampoline_;
UniquePtr<const std::vector<uint8_t> > quick_to_interpreter_bridge_;
@@ -198,8 +242,10 @@ class OatWriter {
uint32_t size_interpreter_to_interpreter_bridge_;
uint32_t size_interpreter_to_compiled_code_bridge_;
uint32_t size_jni_dlsym_lookup_;
+ uint32_t size_portable_imt_conflict_trampoline_;
uint32_t size_portable_resolution_trampoline_;
uint32_t size_portable_to_interpreter_bridge_;
+ uint32_t size_quick_imt_conflict_trampoline_;
uint32_t size_quick_resolution_trampoline_;
uint32_t size_quick_to_interpreter_bridge_;
uint32_t size_trampoline_alignment_;
@@ -214,7 +260,9 @@ class OatWriter {
uint32_t size_oat_dex_file_location_checksum_;
uint32_t size_oat_dex_file_offset_;
uint32_t size_oat_dex_file_methods_offsets_;
+ uint32_t size_oat_class_type_;
uint32_t size_oat_class_status_;
+ uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
// Code mappings for deduplication. Deduplication is already done on a pointer basis by the
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index f3d35d728c..638e0ec457 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -18,62 +18,66 @@
#define ART_COMPILER_UTILS_DEDUPE_SET_H_
#include <set>
+#include <string>
#include "base/mutex.h"
#include "base/stl_util.h"
+#include "base/stringprintf.h"
namespace art {
-// A simple data structure to handle hashed deduplication. Add is thread safe.
-template <typename Key, typename HashType, typename HashFunc>
+// 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 Key, typename HashType, typename HashFunc, HashType kShard = 1>
class DedupeSet {
typedef std::pair<HashType, Key*> HashedKey;
class Comparator {
public:
bool operator()(const HashedKey& a, const HashedKey& b) const {
- if (a.first < b.first) return true;
- if (a.first > b.first) return true;
- return *a.second < *b.second;
+ if (a.first != b.first) {
+ return a.first < b.first;
+ } else {
+ return *a.second < *b.second;
+ }
}
};
- typedef std::set<HashedKey, Comparator> Keys;
-
public:
- typedef typename Keys::iterator iterator;
- typedef typename Keys::const_iterator const_iterator;
- typedef typename Keys::size_type size_type;
- typedef typename Keys::value_type value_type;
-
- iterator begin() { return keys_.begin(); }
- const_iterator begin() const { return keys_.begin(); }
- iterator end() { return keys_.end(); }
- const_iterator end() const { return keys_.end(); }
-
Key* Add(Thread* self, const Key& key) {
- HashType hash = HashFunc()(key);
- HashedKey hashed_key(hash, const_cast<Key*>(&key));
- MutexLock lock(self, lock_);
- auto it = keys_.find(hashed_key);
- if (it != keys_.end()) {
+ HashType raw_hash = HashFunc()(key);
+ HashType shard_hash = raw_hash / kShard;
+ HashType shard_bin = raw_hash % kShard;
+ HashedKey hashed_key(shard_hash, const_cast<Key*>(&key));
+ MutexLock lock(self, *lock_[shard_bin]);
+ auto it = keys_[shard_bin].find(hashed_key);
+ if (it != keys_[shard_bin].end()) {
return it->second;
}
hashed_key.second = new Key(key);
- keys_.insert(hashed_key);
+ keys_[shard_bin].insert(hashed_key);
return hashed_key.second;
}
- DedupeSet() : lock_("dedupe lock") {
+ explicit DedupeSet(const char* set_name) {
+ for (HashType i = 0; i < kShard; ++i) {
+ lock_name_[i] = StringPrintf("%s lock %d", set_name, i);
+ lock_[i].reset(new Mutex(lock_name_[i].c_str()));
+ }
}
~DedupeSet() {
- STLDeleteValues(&keys_);
+ for (HashType i = 0; i < kShard; ++i) {
+ STLDeleteValues(&keys_[i]);
+ }
}
private:
- Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- Keys keys_;
+ std::string lock_name_[kShard];
+ UniquePtr<Mutex> lock_[kShard];
+ std::set<HashedKey, Comparator> keys_[kShard];
+
DISALLOW_COPY_AND_ASSIGN(DedupeSet);
};
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index 9f5e292f53..8abe6debc1 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -14,15 +14,12 @@
* limitations under the License.
*/
-#include "common_test.h"
#include "dedupe_set.h"
+#include "gtest/gtest.h"
+#include "thread-inl.h"
namespace art {
-class DedupeSetTest : public testing::Test {
- public:
-};
-
class DedupeHashFunc {
public:
size_t operator()(const std::vector<uint8_t>& array) const {
@@ -35,10 +32,10 @@ class DedupeHashFunc {
return hash;
}
};
-TEST_F(DedupeSetTest, Test) {
+TEST(DedupeSetTest, Test) {
Thread* self = Thread::Current();
typedef std::vector<uint8_t> ByteArray;
- DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator;
+ DedupeSet<ByteArray, size_t, DedupeHashFunc> deduplicator("test");
ByteArray* array1;
{
ByteArray test1;
diff --git a/dalvikvm/Android.mk b/dalvikvm/Android.mk
index 52584cf5e6..a046391885 100644
--- a/dalvikvm/Android.mk
+++ b/dalvikvm/Android.mk
@@ -38,7 +38,7 @@ LOCAL_CPP_EXTENSION := cc
LOCAL_SRC_FILES := dalvikvm.cc
LOCAL_CFLAGS := $(dalvikvm_cflags)
LOCAL_SHARED_LIBRARIES := libnativehelper
-LOCAL_LDFLAGS := -ldl
+LOCAL_LDFLAGS := -ldl -lpthread
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_EXECUTABLE)
ART_HOST_EXECUTABLES += $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index c4cce2ff73..1472337615 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -131,9 +131,13 @@ static void Usage(const char* fmt, ...) {
UsageError(" Example: --instruction-set=x86");
UsageError(" Default: arm");
UsageError("");
+ UsageError(" --instruction-set-features=...,: Specify instruction set features");
+ UsageError(" Example: --instruction-set-features=div");
+ UsageError(" Default: default");
+ UsageError("");
UsageError(" --compiler-backend=(Quick|QuickGBC|Portable): select compiler backend");
UsageError(" set.");
- UsageError(" Example: --instruction-set=Portable");
+ UsageError(" Example: --compiler-backend=Portable");
UsageError(" Default: Quick");
UsageError("");
UsageError(" --host: used with Portable backend to link against host runtime libraries");
@@ -155,13 +159,15 @@ class Dex2Oat {
Runtime::Options& options,
CompilerBackend compiler_backend,
InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
size_t thread_count)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
if (!CreateRuntime(options, instruction_set)) {
*p_dex2oat = NULL;
return false;
}
- *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set, thread_count);
+ *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set,
+ instruction_set_features, thread_count);
return true;
}
@@ -200,21 +206,24 @@ class Dex2Oat {
}
// Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
- CompilerDriver::DescriptorSet* ReadImageClassesFromZip(const std::string& zip_filename,
- const char* image_classes_filename) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename));
+ CompilerDriver::DescriptorSet* ReadImageClassesFromZip(const char* zip_filename,
+ const char* image_classes_filename,
+ std::string* error_msg) {
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
if (zip_archive.get() == NULL) {
- LOG(ERROR) << "Failed to open zip file " << zip_filename;
return NULL;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename));
if (zip_entry.get() == NULL) {
- LOG(ERROR) << "Failed to find " << image_classes_filename << " within " << zip_filename;
+ *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
+ zip_filename, error_msg->c_str());
return NULL;
}
- UniquePtr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(image_classes_filename));
+ UniquePtr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(image_classes_filename,
+ error_msg));
if (image_classes_file.get() == NULL) {
- LOG(ERROR) << "Failed to extract " << image_classes_filename << " from " << zip_filename;
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
+ zip_filename, error_msg->c_str());
return NULL;
}
const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
@@ -254,6 +263,7 @@ class Dex2Oat {
UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_backend_,
instruction_set_,
+ instruction_set_features_,
image,
image_classes.release(),
thread_count_,
@@ -327,9 +337,11 @@ class Dex2Oat {
explicit Dex2Oat(Runtime* runtime,
CompilerBackend compiler_backend,
InstructionSet instruction_set,
+ InstructionSetFeatures instruction_set_features,
size_t thread_count)
: compiler_backend_(compiler_backend),
instruction_set_(instruction_set),
+ instruction_set_features_(instruction_set_features),
runtime_(runtime),
thread_count_(thread_count),
start_ns_(NanoTime()) {
@@ -342,10 +354,6 @@ class Dex2Oat {
return false;
}
Runtime* runtime = Runtime::Current();
- // if we loaded an existing image, we will reuse values from the image roots.
- if (!runtime->HasResolutionMethod()) {
- runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
- }
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
if (!runtime->HasCalleeSaveMethod(type)) {
@@ -368,9 +376,10 @@ class Dex2Oat {
if (DexFilesContains(dex_files, parsed[i])) {
continue;
}
- const DexFile* dex_file = DexFile::Open(parsed[i], parsed[i]);
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(parsed[i].c_str(), parsed[i].c_str(), &error_msg);
if (dex_file == NULL) {
- LOG(WARNING) << "Failed to open dex file " << parsed[i];
+ LOG(WARNING) << "Failed to open dex file '" << parsed[i] << "': " << error_msg;
} else {
dex_files.push_back(dex_file);
}
@@ -391,6 +400,7 @@ class Dex2Oat {
const CompilerBackend compiler_backend_;
const InstructionSet instruction_set_;
+ const InstructionSetFeatures instruction_set_features_;
Runtime* runtime_;
size_t thread_count_;
@@ -416,9 +426,10 @@ static size_t OpenDexFiles(const std::vector<const char*>& dex_filenames,
for (size_t i = 0; i < dex_filenames.size(); i++) {
const char* dex_filename = dex_filenames[i];
const char* dex_location = dex_locations[i];
- const DexFile* dex_file = DexFile::Open(dex_filename, dex_location);
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(dex_filename, dex_location, &error_msg);
if (dex_file == NULL) {
- LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "'\n";
+ LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg;
++failure_count;
} else {
dex_files.push_back(dex_file);
@@ -558,6 +569,32 @@ class WatchDog {
const unsigned int WatchDog::kWatchDogWarningSeconds;
const unsigned int WatchDog::kWatchDogTimeoutSeconds;
+// Given a set of instruction features from the build, parse it. The
+// input 'str' is a comma separated list of feature names. Parse it and
+// return the InstructionSetFeatures object.
+static InstructionSetFeatures ParseFeatureList(std::string str) {
+ InstructionSetFeatures result;
+ typedef std::vector<std::string> FeatureList;
+ FeatureList features;
+ Split(str, ',', features);
+ for (FeatureList::iterator i = features.begin(); i != features.end(); i++) {
+ std::string feature = Trim(*i);
+ if (feature == "default") {
+ // Nothing to do.
+ } else if (feature == "div") {
+ // Supports divide instruction.
+ result.SetHasDivideInstruction(true);
+ } else if (feature == "nodiv") {
+ // Turn off support for divide instruction.
+ result.SetHasDivideInstruction(false);
+ } else {
+ Usage("Unknown instruction set feature: '%s'", feature.c_str());
+ }
+ }
+ // others...
+ return result;
+}
+
static int dex2oat(int argc, char** argv) {
base::TimingLogger timings("compiler", false, false);
@@ -594,6 +631,15 @@ static int dex2oat(int argc, char** argv) {
#else
CompilerBackend compiler_backend = kQuick;
#endif
+
+ // Take the default set of instruction features from the build if present.
+ InstructionSetFeatures instruction_set_features =
+#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES
+ ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES));
+#else
+ ParseFeatureList("default");
+#endif
+
#if defined(__arm__)
InstructionSet instruction_set = kThumb2;
#elif defined(__i386__)
@@ -603,8 +649,10 @@ static int dex2oat(int argc, char** argv) {
#else
#error "Unsupported architecture"
#endif
+
+
bool is_host = false;
- bool dump_stats = kIsDebugBuild;
+ bool dump_stats = false;
bool dump_timing = false;
bool dump_slow_timing = kIsDebugBuild;
bool watch_dog_enabled = !kIsTargetBuild;
@@ -677,6 +725,9 @@ static int dex2oat(int argc, char** argv) {
} else if (instruction_set_str == "x86") {
instruction_set = kX86;
}
+ } else if (option.starts_with("--instruction-set-features=")) {
+ StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+ instruction_set_features = ParseFeatureList(str.as_string());
} else if (option.starts_with("--compiler-backend=")) {
StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
if (backend_str == "Quick") {
@@ -696,6 +747,8 @@ static int dex2oat(int argc, char** argv) {
runtime_args.push_back(argv[i]);
} else if (option == "--dump-timing") {
dump_timing = true;
+ } else if (option == "--dump-stats") {
+ dump_stats = true;
} else {
Usage("Unknown argument %s", option.data());
}
@@ -867,7 +920,8 @@ static int dex2oat(int argc, char** argv) {
#endif
Dex2Oat* p_dex2oat;
- if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set, thread_count)) {
+ if (!Dex2Oat::Create(&p_dex2oat, options, compiler_backend, instruction_set,
+ instruction_set_features, thread_count)) {
LOG(ERROR) << "Failed to create dex2oat";
return EXIT_FAILURE;
}
@@ -885,14 +939,17 @@ static int dex2oat(int argc, char** argv) {
// If --image-classes was specified, calculate the full list of classes to include in the image
UniquePtr<CompilerDriver::DescriptorSet> image_classes(NULL);
if (image_classes_filename != NULL) {
+ std::string error_msg;
if (image_classes_zip_filename != NULL) {
image_classes.reset(dex2oat->ReadImageClassesFromZip(image_classes_zip_filename,
- image_classes_filename));
+ image_classes_filename,
+ &error_msg));
} else {
image_classes.reset(dex2oat->ReadImageClassesFromFile(image_classes_filename));
}
if (image_classes.get() == NULL) {
- LOG(ERROR) << "Failed to create list of image classes from " << image_classes_filename;
+ LOG(ERROR) << "Failed to create list of image classes from '" << image_classes_filename <<
+ "': " << error_msg;
return EXIT_FAILURE;
}
}
@@ -902,14 +959,18 @@ static int dex2oat(int argc, char** argv) {
dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath();
} else {
if (dex_filenames.empty()) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd));
+ std::string error_msg;
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(zip_fd, zip_location.c_str(),
+ &error_msg));
if (zip_archive.get() == NULL) {
- LOG(ERROR) << "Failed to open zip from file descriptor for " << zip_location;
+ LOG(ERROR) << "Failed to open zip from file descriptor for '" << zip_location << "': "
+ << error_msg;
return EXIT_FAILURE;
}
- const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location);
+ const DexFile* dex_file = DexFile::Open(*zip_archive.get(), zip_location, &error_msg);
if (dex_file == NULL) {
- LOG(ERROR) << "Failed to open dex from file descriptor for zip file: " << zip_location;
+ LOG(ERROR) << "Failed to open dex from file descriptor for zip file '" << zip_location
+ << "': " << error_msg;
return EXIT_FAILURE;
}
dex_files.push_back(dex_file);
@@ -1061,7 +1122,8 @@ static int dex2oat(int argc, char** argv) {
// Strip unneeded sections for target
off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
CHECK_EQ(0, seek_actual);
- ElfStripper::Strip(oat_file.get());
+ std::string error_msg;
+ CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
// We wrote the oat file successfully, and want to keep it.
@@ -1082,8 +1144,6 @@ static int dex2oat(int argc, char** argv) {
return EXIT_SUCCESS;
}
-
-
} // namespace art
int main(int argc, char** argv) {
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
new file mode 100644
index 0000000000..f8001a4524
--- /dev/null
+++ b/disassembler/Android.mk
@@ -0,0 +1,120 @@
+#
+# Copyright (C) 2012 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.common.mk
+
+LIBART_DISASSEMBLER_SRC_FILES := \
+ disassembler.cc \
+ disassembler_arm.cc \
+ disassembler_mips.cc \
+ disassembler_x86.cc
+
+# $(1): target or host
+# $(2): ndebug or debug
+define build-libart-disassembler
+ ifneq ($(1),target)
+ ifneq ($(1),host)
+ $$(error expected target or host for argument 1, received $(1))
+ endif
+ endif
+ ifneq ($(2),ndebug)
+ ifneq ($(2),debug)
+ $$(error expected ndebug or debug for argument 2, received $(2))
+ endif
+ endif
+
+ art_target_or_host := $(1)
+ art_ndebug_or_debug := $(2)
+
+ include $(CLEAR_VARS)
+ ifeq ($$(art_target_or_host),target)
+ include external/stlport/libstlport.mk
+ else
+ LOCAL_IS_HOST_MODULE := true
+ endif
+ LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION)
+ ifeq ($$(art_ndebug_or_debug),ndebug)
+ LOCAL_MODULE := libart-disassembler
+ else # debug
+ LOCAL_MODULE := libartd-disassembler
+ endif
+
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+
+ LOCAL_SRC_FILES := $$(LIBART_DISASSEMBLER_SRC_FILES)
+
+ GENERATED_SRC_DIR := $$(call intermediates-dir-for,$$(LOCAL_MODULE_CLASS),$$(LOCAL_MODULE),$$(LOCAL_IS_HOST_MODULE),)
+
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_CLANG := $(ART_TARGET_CLANG)
+ LOCAL_CFLAGS += $(ART_TARGET_CFLAGS)
+ else # host
+ LOCAL_CLANG := $(ART_HOST_CLANG)
+ LOCAL_CFLAGS += $(ART_HOST_CFLAGS)
+ endif
+
+ LOCAL_SHARED_LIBRARIES += liblog
+ ifeq ($$(art_ndebug_or_debug),debug)
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_CFLAGS += $(ART_TARGET_DEBUG_CFLAGS)
+ else # host
+ LOCAL_CFLAGS += $(ART_HOST_DEBUG_CFLAGS)
+ endif
+ LOCAL_SHARED_LIBRARIES += libartd
+ else
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_CFLAGS += $(ART_TARGET_NON_DEBUG_CFLAGS)
+ else # host
+ LOCAL_CFLAGS += $(ART_HOST_NON_DEBUG_CFLAGS)
+ endif
+ LOCAL_SHARED_LIBRARIES += libart
+ endif
+
+ LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
+
+ LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ ifeq ($$(art_target_or_host),target)
+ LOCAL_SHARED_LIBRARIES += libcutils
+ include $(LLVM_GEN_INTRINSICS_MK)
+ include $(LLVM_DEVICE_BUILD_MK)
+ include $(BUILD_SHARED_LIBRARY)
+ else # host
+ LOCAL_STATIC_LIBRARIES += libcutils
+ include $(LLVM_GEN_INTRINSICS_MK)
+ include $(LLVM_HOST_BUILD_MK)
+ include $(BUILD_HOST_SHARED_LIBRARY)
+ endif
+endef
+
+ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
+ $(eval $(call build-libart-disassembler,target,ndebug))
+endif
+ifeq ($(ART_BUILD_TARGET_DEBUG),true)
+ $(eval $(call build-libart-disassembler,target,debug))
+endif
+ifeq ($(WITH_HOST_DALVIK),true)
+ # We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+ ifeq ($(ART_BUILD_NDEBUG),true)
+ $(eval $(call build-libart-disassembler,host,ndebug))
+ endif
+ ifeq ($(ART_BUILD_DEBUG),true)
+ $(eval $(call build-libart-disassembler,host,debug))
+ endif
+endif
diff --git a/runtime/disassembler.cc b/disassembler/disassembler.cc
index 067083510b..067083510b 100644
--- a/runtime/disassembler.cc
+++ b/disassembler/disassembler.cc
diff --git a/runtime/disassembler.h b/disassembler/disassembler.h
index 805ff4d079..7547ab722b 100644
--- a/runtime/disassembler.h
+++ b/disassembler/disassembler.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_H_
-#define ART_RUNTIME_DISASSEMBLER_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_H_
#include <stdint.h>
@@ -45,4 +45,4 @@ class Disassembler {
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_H_
diff --git a/runtime/disassembler_arm.cc b/disassembler/disassembler_arm.cc
index 879d3ac71c..8d4f3ce263 100644
--- a/runtime/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -103,6 +103,10 @@ static const char* kThumbDataProcessingOperations[] = {
"tst", "rsb", "cmp", "cmn", "orr", "mul", "bic", "mvn",
};
+static const char* kThumbReverseOperations[] = {
+ "rev", "rev16", "rbit", "revsh"
+};
+
struct ArmRegister {
explicit ArmRegister(uint32_t r) : r(r) { CHECK_LE(r, 15U); }
ArmRegister(uint32_t instruction, uint32_t at_bit) : r((instruction >> at_bit) & 0xf) { CHECK_LE(r, 15U); }
@@ -278,6 +282,26 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) {
os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
}
+int32_t ThumbExpand(int32_t imm12) {
+ if ((imm12 & 0xC00) == 0) {
+ switch ((imm12 >> 8) & 3) {
+ case 0:
+ return imm12 & 0xFF;
+ case 1:
+ return ((imm12 & 0xFF) << 16) | (imm12 & 0xFF);
+ case 2:
+ return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 8);
+ default: // 3
+ return ((imm12 & 0xFF) << 24) | ((imm12 & 0xFF) << 16) | ((imm12 & 0xFF) << 8) |
+ (imm12 & 0xFF);
+ }
+ } else {
+ uint32_t val = 0x80 | (imm12 & 0x7F);
+ int32_t rotate = (imm12 >> 7) & 0x1F;
+ return (val >> rotate) | (val << (32 - rotate));
+ }
+}
+
size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
uint32_t instr = (ReadU16(instr_ptr) << 16) | ReadU16(instr_ptr + 2);
// |111|1 1|1000000|0000|1111110000000000|
@@ -350,7 +374,102 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
// uint32_t op5 = (instr >> 4) & 0xF;
ArmRegister Rn(instr, 16);
ArmRegister Rt(instr, 12);
+ ArmRegister Rd(instr, 8);
uint32_t imm8 = instr & 0xFF;
+ if ((op3 & 2) == 2) { // 1x
+ int W = (instr >> 21) & 1;
+ int U = (instr >> 23) & 1;
+ int P = (instr >> 24) & 1;
+
+ if ((op4 & 1) == 1) {
+ opcode << "ldrd";
+ } else {
+ opcode << "strd";
+ }
+ args << Rt << "," << Rd << ", [" << Rn;
+ const char *sign = U ? "+" : "-";
+ if (P == 0 && W == 1) {
+ args << "], #" << sign << imm8;
+ } else {
+ args << ", #" << sign << imm8 << "]";
+ if (W == 1) {
+ args << "!";
+ }
+ }
+ } else { // 0x
+ switch (op4) {
+ case 0:
+ if (op3 == 0) { // op3 is 00, op4 is 00
+ opcode << "strex";
+ args << Rd << ", " << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
+ } else { // op3 is 01, op4 is 00
+ // this is one of strexb, strexh or strexd
+ int op5 = (instr >> 4) & 0xf;
+ switch (op5) {
+ case 4:
+ opcode << "strexb";
+ break;
+ case 5:
+ opcode << "strexh";
+ break;
+ case 7:
+ opcode << "strexd";
+ break;
+ }
+ }
+ break;
+ case 1:
+ if (op3 == 0) { // op3 is 00, op4 is 01
+ opcode << "ldrex";
+ args << Rt << ", [" << Rn << ", #" << (imm8 << 2) << "]";
+ } else { // op3 is 01, op4 is 01
+ // this is one of strexb, strexh or strexd
+ int op5 = (instr >> 4) & 0xf;
+ switch (op5) {
+ case 0:
+ opcode << "tbb";
+ break;
+ case 1:
+ opcode << "tbh";
+ break;
+ case 4:
+ opcode << "ldrexb";
+ break;
+ case 5:
+ opcode << "ldrexh";
+ break;
+ case 7:
+ opcode << "ldrexd";
+ break;
+ }
+ }
+ break;
+ case 2: // op3 is 0x, op4 is 10
+ case 3: // op3 is 0x, op4 is 11
+ if (op4 == 2) {
+ opcode << "strd";
+ } else {
+ opcode << "ldrd";
+ }
+ int W = (instr >> 21) & 1;
+ int U = (instr >> 23) & 1;
+ int P = (instr >> 24) & 1;
+
+ args << Rt << "," << Rd << ", [" << Rn;
+ const char *sign = U ? "+" : "-";
+ if (P == 0 && W == 1) {
+ args << "], #" << sign << imm8;
+ } else {
+ args << ", #" << sign << imm8 << "]";
+ if (W == 1) {
+ args << "!";
+ }
+ }
+ break;
+ }
+ }
+
+
if (op3 == 0 && op4 == 0) { // STREX
ArmRegister Rd(instr, 8);
opcode << "strex";
@@ -495,19 +614,11 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
uint32_t op3 = (instr >> 20) & 0x3F;
uint32_t coproc = (instr >> 8) & 0xF;
uint32_t op4 = (instr >> 4) & 0x1;
- if ((op3 == 2 || op3 == 2 || op3 == 6 || op3 == 7) || // 00x1x
- (op3 >= 8 && op3 <= 15) || (op3 >= 16 && op3 <= 31)) { // 001xxx, 01xxxx
- // Extension register load/store instructions
- // |111|1|110|00000|0000|1111|110|000000000|
- // |5 3|2|109|87654|3 0|54 2|10 |87 54 0|
- // |---|-|---|-----|----|----|---|---------|
- // |332|2|222|22222|1111|1111|110|000000000|
- // |1 9|8|765|43210|9 6|54 2|10 |87 54 0|
- // |---|-|---|-----|----|----|---|---------|
- // |111|T|110| op3 | Rn | |101| |
- // 111 0 110 01001 0011 0000 101 000000011 - ec930a03
- if (op3 == 9 || op3 == 0xD) { // VLDM
- // 1110 110 PUDW1 nnnn dddd 101S iiii iiii
+
+ if (coproc == 10 || coproc == 11) { // 101x
+ if (op3 < 0x20 && (op3 >> 1) != 2) { // 0xxxxx and not 00010x
+ // extension load/store instructions
+ int op = op3 & 0x1f;
uint32_t P = (instr >> 24) & 1;
uint32_t U = (instr >> 23) & 1;
uint32_t D = (instr >> 22) & 1;
@@ -517,20 +628,49 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
uint32_t Vd = (instr >> 12) & 0xF;
uint32_t imm8 = instr & 0xFF;
uint32_t d = (S == 0 ? ((Vd << 1) | D) : (Vd | (D << 4)));
- if (P == 0 && U == 0 && W == 0) {
- // TODO: 64bit transfers between ARM core and extension registers.
- } else if (P == 0 && U == 1 && Rn.r == 13) { // VPOP
- opcode << "vpop" << (S == 0 ? ".f64" : ".f32");
- args << d << " .. " << (d + imm8);
- } else if (P == 1 && W == 0) { // VLDR
- opcode << "vldr" << (S == 0 ? ".f64" : ".f32");
- args << d << ", [" << Rn << ", #" << imm8 << "]";
- } else { // VLDM
- opcode << "vldm" << (S == 0 ? ".f64" : ".f32");
- args << Rn << ", " << d << " .. " << (d + imm8);
+ ArmRegister Rd(d, 0);
+
+ if (op == 8 || op == 12 || op == 10 || op == 14 ||
+ op == 18 || op == 22) { // 01x00 or 01x10
+ // vector store multiple or vpush
+ if (P == 1 && U == 0 && W == 1 && Rn.r == 13) {
+ opcode << "vpush" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << " .. " << (Rd.r + imm8);
+ } else {
+ opcode << "vstm" << (S == 0 ? ".f64" : ".f32");
+ args << Rn << ", " << Rd << " .. " << (Rd.r + imm8);
+ }
+ } else if (op == 16 || op == 20 || op == 24 || op == 28) {
+ // 1xx00
+ // vector store register
+ opcode << "vstr" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << ", [" << Rn << ", #" << imm8 << "]";
+ } else if (op == 17 || op == 21 || op == 25 || op == 29) {
+ // 1xx01
+ // vector load register
+ opcode << "vldr" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << ", [" << Rn << ", #" << imm8 << "]";
+ } else if (op == 9 || op == 13 || op == 11 || op == 15 ||
+ op == 19 || op == 23 ) { // 01x11 10x11
+ // vldm or vpop
+ if (P == 1 && U == 0 && W == 1 && Rn.r == 13) {
+ opcode << "vpop" << (S == 0 ? ".f64" : ".f32");
+ args << Rd << " .. " << (Rd.r + imm8);
+ } else {
+ opcode << "vldm" << (S == 0 ? ".f64" : ".f32");
+ args << Rn << ", " << Rd << " .. " << (Rd.r + imm8);
+ }
}
+ } else if ((op3 >> 1) == 2) { // 00010x
+ // 64 bit transfers
+ } else if ((op3 >> 4) == 2 && op4 == 0) { // 10xxxx, op = 0
+ // fp data processing
+ } else if ((op3 >> 4) == 2 && op4 == 1) { // 10xxxx, op = 1
+ // 8,16,32 bit transfers
}
- } else if ((op3 & 0x30) == 0x20 && op4 == 0) { // 10 xxxx ... 0
+ }
+
+ if ((op3 & 0x30) == 0x20 && op4 == 0) { // 10 xxxx ... 0
if ((coproc & 0xE) == 0xA) {
// VFP data-processing instructions
// |111|1|1100|0000|0000|1111|110|0|00 |0|0|0000|
@@ -628,7 +768,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
opcode << "s";
}
}
- args << Rd << ", ThumbExpand(" << imm32 << ")";
+ args << Rd << ", #" << ThumbExpand(imm32);
} else if (Rd.r == 0xF && S == 1 &&
(op3 == 0x0 || op3 == 0x4 || op3 == 0x8 || op3 == 0xD)) {
if (op3 == 0x0) {
@@ -640,7 +780,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
} else {
opcode << "cmp.w";
}
- args << Rn << ", ThumbExpand(" << imm32 << ")";
+ args << Rn << ", #" << ThumbExpand(imm32);
} else {
switch (op3) {
case 0x0: opcode << "and"; break;
@@ -658,7 +798,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
if (S == 1) {
opcode << "s";
}
- args << Rd << ", " << Rn << ", ThumbExpand(" << imm32 << ")";
+ args << Rd << ", " << Rn << ", #" << ThumbExpand(imm32);
}
} else if ((instr & 0x8000) == 0 && (op2 & 0x20) != 0) {
// Data-processing (plain binary immediate)
@@ -975,6 +1115,31 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
}
break;
}
+ case 0x29: { // 0101001
+ // |111|11|1000000|0000|1111|1100|00|0 0|0000|
+ // |5 3|21|0 4|3 0|5 2|1 8|76|5 4|3 0|
+ // |---|--|-------|----|----|----|--|---|----|
+ // |332|22|2222222|1111|1111|1100|00|0 0|0000|
+ // |1 9|87|6 0|9 6|5 2|1 8|76|5 4|3 0|
+ // |---|--|-------|----|----|----|--|---|----|
+ // |111|11|0101001| Rm |1111| Rd |11|op3| Rm |
+ // REV - 111 11 0101001 mmmm 1111 dddd 1000 mmmm
+ // REV16 - 111 11 0101001 mmmm 1111 dddd 1001 mmmm
+ // RBIT - 111 11 0101001 mmmm 1111 dddd 1010 mmmm
+ // REVSH - 111 11 0101001 mmmm 1111 dddd 1011 mmmm
+ if ((instr & 0xf0c0) == 0xf080) {
+ uint32_t op3 = (instr >> 4) & 3;
+ opcode << kThumbReverseOperations[op3];
+ ArmRegister Rm(instr, 0);
+ ArmRegister Rd(instr, 8);
+ args << Rd << ", " << Rm;
+ ArmRegister Rm2(instr, 16);
+ if (Rm.r != Rm2.r || Rm.r == 13 || Rm.r == 15 || Rd.r == 13 || Rd.r == 15) {
+ args << " (UNPREDICTABLE)";
+ }
+ } // else unknown instruction
+ break;
+ }
case 0x05: case 0x0D: case 0x15: case 0x1D: { // 00xx101
// Load word
// |111|11|10|0 0|00|0|0000|1111|110000|000000|
@@ -1021,6 +1186,72 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr)
}
break;
}
+ default: // more formats
+ if ((op2 >> 4) == 2) { // 010xxxx
+ // data processing (register)
+ } else if ((op2 >> 3) == 6) { // 0110xxx
+ // Multiply, multiply accumulate, and absolute difference
+ op1 = (instr >> 20) & 0x7;
+ op2 = (instr >> 4) & 0x2;
+ ArmRegister Ra(instr, 12);
+ ArmRegister Rn(instr, 16);
+ ArmRegister Rm(instr, 0);
+ ArmRegister Rd(instr, 8);
+ switch (op1) {
+ case 0:
+ if (op2 == 0) {
+ if (Ra.r == 0xf) {
+ opcode << "mul";
+ args << Rd << ", " << Rn << ", " << Rm;
+ } else {
+ opcode << "mla";
+ args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
+ }
+ } else {
+ opcode << "mls";
+ args << Rd << ", " << Rn << ", " << Rm << ", " << Ra;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ break; // do these sometime
+ }
+ } else if ((op2 >> 3) == 7) { // 0111xxx
+ // Long multiply, long multiply accumulate, and divide
+ op1 = (instr >> 20) & 0x7;
+ op2 = (instr >> 4) & 0xf;
+ ArmRegister Rn(instr, 16);
+ ArmRegister Rm(instr, 0);
+ ArmRegister Rd(instr, 8);
+ ArmRegister RdHi(instr, 8);
+ ArmRegister RdLo(instr, 12);
+ switch (op1) {
+ case 0:
+ opcode << "smull";
+ args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
+ break;
+ case 1:
+ opcode << "sdiv";
+ args << Rd << ", " << Rn << ", " << Rm;
+ break;
+ case 2:
+ opcode << "umull";
+ args << RdLo << ", " << RdHi << ", " << Rn << ", " << Rm;
+ break;
+ case 3:
+ opcode << "udiv";
+ args << Rd << ", " << Rn << ", " << Rm;
+ break;
+ case 4:
+ case 5:
+ case 6:
+ break; // TODO: when we generate these...
+ }
+ }
}
default:
break;
@@ -1265,6 +1496,16 @@ size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr)
DumpBranchTarget(args, instr_ptr + 4, imm32);
break;
}
+ case 0x50: case 0x51: // 101000x
+ case 0x52: case 0x53: // 101001x
+ case 0x56: case 0x57: { // 101011x
+ uint16_t op = (instr >> 6) & 3;
+ opcode << kThumbReverseOperations[op];
+ ThumbRegister Rm(instr, 3);
+ ThumbRegister Rd(instr, 0);
+ args << Rd << ", " << Rm;
+ break;
+ }
case 0x78: case 0x79: case 0x7A: case 0x7B: // 1111xxx
case 0x7C: case 0x7D: case 0x7E: case 0x7F: {
// If-Then, and hints
diff --git a/runtime/disassembler_arm.h b/disassembler/disassembler_arm.h
index cab9150108..2e699ffe88 100644
--- a/runtime/disassembler_arm.h
+++ b/disassembler/disassembler_arm.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_ARM_H_
-#define ART_RUNTIME_DISASSEMBLER_ARM_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_ARM_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_ARM_H_
#include <vector>
@@ -48,4 +48,4 @@ class DisassemblerArm : public Disassembler {
} // namespace arm
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_ARM_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_ARM_H_
diff --git a/runtime/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 25bbae68ef..25bbae68ef 100644
--- a/runtime/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
diff --git a/runtime/disassembler_mips.h b/disassembler/disassembler_mips.h
index e248503963..d3862676a0 100644
--- a/runtime/disassembler_mips.h
+++ b/disassembler/disassembler_mips.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_MIPS_H_
-#define ART_RUNTIME_DISASSEMBLER_MIPS_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_
#include <vector>
@@ -37,4 +37,4 @@ class DisassemblerMips : public Disassembler {
} // namespace mips
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_MIPS_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_MIPS_H_
diff --git a/runtime/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index e5cdb7b297..9ed65cd80a 100644
--- a/runtime/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -520,6 +520,10 @@ DISASSEMBLER_ENTRY(cmp,
case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break;
case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; break;
case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
+ case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF:
+ opcode << "bswap";
+ reg_in_opcode = true;
+ break;
default:
opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
break;
diff --git a/runtime/disassembler_x86.h b/disassembler/disassembler_x86.h
index ff4322c8b8..9adaff7048 100644
--- a/runtime/disassembler_x86.h
+++ b/disassembler/disassembler_x86.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_DISASSEMBLER_X86_H_
-#define ART_RUNTIME_DISASSEMBLER_X86_H_
+#ifndef ART_DISASSEMBLER_DISASSEMBLER_X86_H_
+#define ART_DISASSEMBLER_DISASSEMBLER_X86_H_
#include "disassembler.h"
@@ -35,4 +35,4 @@ class DisassemblerX86 : public Disassembler {
} // namespace x86
} // namespace art
-#endif // ART_RUNTIME_DISASSEMBLER_X86_H_
+#endif // ART_DISASSEMBLER_DISASSEMBLER_X86_H_
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index a63b229846..7cee00e182 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -22,17 +22,17 @@ OATDUMP_SRC_FILES := \
include art/build/Android.executable.mk
ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils,,target,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libart-disassembler,art/disassembler,target,ndebug))
endif
ifeq ($(ART_BUILD_TARGET_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils,,target,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug))
endif
ifeq ($(WITH_HOST_DALVIK),true)
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),,,host,ndebug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug))
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
- $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),,,host,debug))
+ $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug))
endif
endif
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 304222ac8a..b9716d556f 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -86,6 +86,8 @@ static void usage() {
const char* image_roots_descriptions_[] = {
"kResolutionMethod",
+ "kImtConflictMethod",
+ "kDefaultImt",
"kCalleeSaveMethod",
"kRefsOnlySaveMethod",
"kRefsAndArgsSaveMethod",
@@ -116,6 +118,9 @@ class OatDumper {
os << "INSTRUCTION SET:\n";
os << oat_header.GetInstructionSet() << "\n\n";
+ os << "INSTRUCTION SET FEATURES:\n";
+ os << oat_header.GetInstructionSetFeatures().GetFeatureString() << "\n\n";
+
os << "DEX FILE COUNT:\n";
os << oat_header.GetDexFileCount() << "\n\n";
@@ -173,9 +178,13 @@ class OatDumper {
MethodHelper mh(m);
for (size_t i = 0; i < oat_dex_files_.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
- CHECK(oat_dex_file != NULL);
- UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
- if (dex_file.get() != NULL) {
+ CHECK(oat_dex_file != nullptr);
+ std::string error_msg;
+ UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
+ << "': " << error_msg;
+ } else {
const DexFile::ClassDef* class_def =
dex_file->FindClassDef(mh.GetDeclaringClassDescriptor());
if (class_def != NULL) {
@@ -199,8 +208,11 @@ class OatDumper {
for (size_t i = 0; i < oat_dex_files_.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
CHECK(oat_dex_file != NULL);
- UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
- if (dex_file.get() == NULL) {
+ std::string error_msg;
+ UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile(&error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation()
+ << "': " << error_msg;
continue;
}
offsets_.insert(reinterpret_cast<uint32_t>(&dex_file->GetHeader()));
@@ -245,9 +257,10 @@ class OatDumper {
os << "OAT DEX FILE:\n";
os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
- UniquePtr<const DexFile> dex_file(oat_dex_file.OpenDexFile());
+ std::string error_msg;
+ UniquePtr<const DexFile> dex_file(oat_dex_file.OpenDexFile(&error_msg));
if (dex_file.get() == NULL) {
- os << "NOT FOUND\n\n";
+ os << "NOT FOUND: " << error_msg << "\n\n";
return;
}
for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) {
@@ -255,8 +268,10 @@ class OatDumper {
const char* descriptor = dex_file->GetClassDescriptor(class_def);
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file.GetOatClass(class_def_index));
CHECK(oat_class.get() != NULL);
- os << StringPrintf("%zd: %s (type_idx=%d) (", class_def_index, descriptor, class_def.class_idx_)
- << oat_class->GetStatus() << ")\n";
+ os << StringPrintf("%zd: %s (type_idx=%d)", class_def_index, descriptor, class_def.class_idx_)
+ << " (" << oat_class->GetStatus() << ")"
+ << " (" << oat_class->GetType() << ")\n";
+ // TODO: include bitmap here if type is kOatClassBitmap?
Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indented_os(&indent_filter);
DumpOatClass(indented_os, *oat_class.get(), *(dex_file.get()), class_def);
@@ -727,9 +742,10 @@ class ImageDumper {
os << " (" << oat_location << ")";
}
os << "\n";
- const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location);
+ std::string error_msg;
+ const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location, &error_msg);
if (oat_file == NULL) {
- os << "NOT FOUND\n";
+ os << "NOT FOUND: " << error_msg << "\n";
return;
}
os << "\n";
@@ -775,7 +791,7 @@ class ImageDumper {
os << "STATS:\n" << std::flush;
UniquePtr<File> file(OS::OpenFileForReading(image_filename_.c_str()));
if (file.get() == NULL) {
- std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_));
+ std::string cache_location(GetDalvikCacheFilenameOrDie(image_filename_.c_str()));
file.reset(OS::OpenFileForReading(cache_location.c_str()));
if (file.get() == NULL) {
LOG(WARNING) << "Failed to find image in " << image_filename_
@@ -994,7 +1010,8 @@ class ImageDumper {
indent_os << StringPrintf("OAT CODE: %p\n", oat_code);
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
- method->IsResolutionMethod() || MethodHelper(method).IsClassInitializer()) {
+ method->IsResolutionMethod() || method->IsImtConflictMethod() ||
+ MethodHelper(method).IsClassInitializer()) {
DCHECK(method->GetNativeGcMap() == NULL) << PrettyMethod(method);
DCHECK(method->GetMappingTable() == NULL) << PrettyMethod(method);
} else {
@@ -1126,7 +1143,7 @@ class ImageDumper {
typedef SafeMap<std::string, SizeAndCount> SizeAndCountTable;
SizeAndCountTable sizes_and_counts;
- void Update(const std::string& descriptor, size_t object_bytes) {
+ void Update(const char* descriptor, size_t object_bytes) {
SizeAndCountTable::iterator it = sizes_and_counts.find(descriptor);
if (it != sizes_and_counts.end()) {
it->second.bytes += object_bytes;
@@ -1412,10 +1429,11 @@ static int oatdump(int argc, char** argv) {
}
if (oat_filename != NULL) {
+ std::string error_msg;
OatFile* oat_file =
- OatFile::Open(oat_filename, oat_filename, NULL, false);
+ OatFile::Open(oat_filename, oat_filename, NULL, false, &error_msg);
if (oat_file == NULL) {
- fprintf(stderr, "Failed to open oat file from %s\n", oat_filename);
+ fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
}
OatDumper oat_dumper(*host_prefix.get(), *oat_file);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 8df03cf1af..bef4381c2b 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -21,6 +21,8 @@ include art/build/Android.common.mk
LIBART_COMMON_SRC_FILES := \
atomic.cc.arm \
barrier.cc \
+ base/allocator.cc \
+ base/bit_vector.cc \
base/logging.cc \
base/mutex.cc \
base/stringpiece.cc \
@@ -38,10 +40,6 @@ LIBART_COMMON_SRC_FILES := \
dex_file.cc \
dex_file_verifier.cc \
dex_instruction.cc \
- disassembler.cc \
- disassembler_arm.cc \
- disassembler_mips.cc \
- disassembler_x86.cc \
elf_file.cc \
gc/allocator/dlmalloc.cc \
gc/accounting/card_table.cc \
@@ -64,6 +62,9 @@ LIBART_COMMON_SRC_FILES := \
instrumentation.cc \
intern_table.cc \
interpreter/interpreter.cc \
+ interpreter/interpreter_common.cc \
+ interpreter/interpreter_goto_table_impl.cc \
+ interpreter/interpreter_switch_impl.cc \
jdwp/jdwp_event.cc \
jdwp/jdwp_expand_buf.cc \
jdwp/jdwp_handler.cc \
@@ -186,6 +187,7 @@ LIBART_TARGET_SRC_FILES += \
arch/arm/jni_entrypoints_arm.S \
arch/arm/portable_entrypoints_arm.S \
arch/arm/quick_entrypoints_arm.S \
+ arch/arm/arm_sdiv.S \
arch/arm/thread_arm.cc
else # TARGET_ARCH != arm
ifeq ($(TARGET_ARCH),x86)
@@ -246,7 +248,9 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \
jdwp/jdwp.h \
jdwp/jdwp_constants.h \
locks.h \
+ lock_word.h \
mirror/class.h \
+ oat.h \
thread.h \
thread_state.h \
verifier/method_verifier.h
diff --git a/runtime/arch/arm/arm_sdiv.S b/runtime/arch/arm/arm_sdiv.S
new file mode 100644
index 0000000000..925e428444
--- /dev/null
+++ b/runtime/arch/arm/arm_sdiv.S
@@ -0,0 +1,24 @@
+// This function is used to check for the CPU's support for the sdiv
+// instruction at runtime. It will either return the value 1 or
+// will cause an invalid instruction trap (SIGILL signal). The
+// caller must arrange for the signal handler to set the r0
+// register to 0 and move the pc forward by 4 bytes (to skip
+// the invalid instruction).
+
+
+#include "asm_support_arm.S"
+
+.section .text
+ENTRY CheckForARMSDIVInstruction
+ mov r1,#1
+ // depending on the architecture, the assembler will not allow an
+ // sdiv instruction, so we will have to output the bytes directly.
+
+ // sdiv r0,r1,r1 is two words: 0xfb91 0xf1f0. We need little endian.
+ .byte 0x91,0xfb,0xf1,0xf0
+
+ // if the divide worked, r0 will have the value #1 (result of sdiv).
+ // It will have 0 otherwise (set by the signal handler)
+ // the value is just returned from this function.
+ bx lr
+ END CheckForARMSDIVInstruction
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index ed3d476b24..cfffbea2c5 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -25,7 +25,11 @@
#define rSELF r9
// Offset of field Thread::suspend_count_ verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
+// Offset of field Thread::card_table_ verified in InitCpu
+#define THREAD_CARD_TABLE_OFFSET 8
// Offset of field Thread::exception_ verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 12
+// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
+#define THREAD_ID_OFFSET 60
#endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 9e6902de3f..3dac636df9 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -42,10 +42,16 @@ extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, voi
extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t);
extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+
// Cast entrypoints.
extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
const mirror::Class* ref_class);
-extern "C" void art_quick_can_put_array_element(void*, void*);
extern "C" void art_quick_check_cast(void*, void*);
// DexCache entrypoints.
@@ -71,7 +77,10 @@ extern "C" int64_t art_quick_get64_static(uint32_t);
extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
extern "C" void* art_quick_get_obj_static(uint32_t);
-// FillArray entrypoint.
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
@@ -112,10 +121,10 @@ extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
extern "C" int32_t art_quick_string_compareto(void*, void*);
// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
@@ -133,6 +142,30 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+static bool quick_alloc_entry_points_instrumented = false;
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented) {
+ quick_alloc_entry_points_instrumented = instrumented;
+}
+
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
+ if (quick_alloc_entry_points_instrumented) {
+ qpoints->pAllocArray = art_quick_alloc_array_instrumented;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented;
+ qpoints->pAllocObject = art_quick_alloc_object_instrumented;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented;
+ } else {
+ qpoints->pAllocArray = art_quick_alloc_array;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
+ qpoints->pAllocObject = art_quick_alloc_object;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ }
+}
+
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
// Interpreter
@@ -147,16 +180,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
// Alloc
- qpoints->pAllocArray = art_quick_alloc_array;
- qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
- qpoints->pAllocObject = art_quick_alloc_object;
- qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ ResetQuickAllocEntryPoints(qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
- qpoints->pCanPutArrayElement = art_quick_can_put_array_element;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -179,7 +206,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
- // FillArray
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
@@ -210,7 +240,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pD2l = art_d2l;
qpoints->pF2l = art_f2l;
qpoints->pLdiv = __aeabi_ldivmod;
- qpoints->pLdivmod = __aeabi_ldivmod; // result returned in r2:r3
+ qpoints->pLmod = __aeabi_ldivmod; // result returned in r2:r3
qpoints->pLmul = art_quick_mul_long;
qpoints->pShlLong = art_quick_shl_long;
qpoints->pShrLong = art_quick_shr_long;
@@ -223,10 +253,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pMemcpy = memcpy;
// Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a77ce01562..9a853d07ab 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -48,7 +48,7 @@
/*
* Macro that sets up the callee save frame to conform with
- * Runtime::CreateCalleeSaveMethod(kRefsOnly). Restoration assumes non-moving GC.
+ * Runtime::CreateCalleeSaveMethod(kRefsOnly).
*/
.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME
push {r5-r8, r10-r11, lr} @ 7 words of callee saves
@@ -67,21 +67,21 @@
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- ldr lr, [sp, #28] @ restore lr for return
- add sp, #32 @ unwind stack
+ add sp, #4 @ bottom word holds Method*
+ pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
.cfi_adjust_cfa_offset -32
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
- ldr lr, [sp, #28] @ restore lr for return
- add sp, #32 @ unwind stack
+ add sp, #4 @ bottom word holds Method*
+ pop {r5-r8, r10-r11, lr} @ 7 words of callee saves
.cfi_adjust_cfa_offset -32
- bx lr @ return
+ bx lr @ return
.endm
/*
* Macro that sets up the callee save frame to conform with
- * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). Restoration assumes non-moving GC.
+ * Runtime::CreateCalleeSaveMethod(kRefsAndArgs).
*/
.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
push {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
@@ -103,10 +103,8 @@
.endm
.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
- ldr r1, [sp, #8] @ restore non-callee save r1
- ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ rewind sp
+ add sp, #8 @ rewind sp
+ pop {r1-r3, r5-r8, r10-r11, lr} @ 10 words of callee saves
.cfi_adjust_cfa_offset -48
.endm
@@ -152,6 +150,7 @@ ENTRY \c_name
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
b \cxx_name @ \cxx_name(Thread*, SP)
+ bkpt
END \c_name
.endm
@@ -162,6 +161,7 @@ ENTRY \c_name
mov r2, r9 @ pass Thread::Current
mov r3, sp @ pass SP
b \cxx_name @ \cxx_name(Thread*, SP)
+ bkpt
END \c_name
.endm
@@ -318,22 +318,68 @@ ENTRY art_quick_handle_fill_data
END art_quick_handle_fill_data
/*
- * Entry from managed code that calls artLockObjectFromCode, may block for GC.
+ * Entry from managed code that calls artLockObjectFromCode, may block for GC. r0 holds the
+ * possibly null object to lock.
*/
.extern artLockObjectFromCode
ENTRY art_quick_lock_object
+ cbz r0, slow_lock
+retry_lock:
+ ldr r2, [r9, #THREAD_ID_OFFSET]
+ ldrex r1, [r0, #LOCK_WORD_OFFSET]
+ cbnz r1, not_unlocked @ already thin locked
+ @ unlocked case - r2 holds thread id with count of 0
+ strex r3, r2, [r0, #LOCK_WORD_OFFSET]
+ cbnz r3, strex_fail @ store failed, retry
+ bx lr
+strex_fail:
+ b retry_lock @ unlikely forward branch, need to reload and recheck r1/r2
+not_unlocked:
+ lsr r3, r1, 30
+ cbnz r3, slow_lock @ if either of the top two bits are set, go slow path
+ eor r2, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId()
+ uxth r2, r2 @ zero top 16 bits
+ cbnz r2, slow_lock @ lock word and self thread id's match -> recursive lock
+ @ else contention, go to slow path
+ add r2, r1, #65536 @ increment count in lock word placing in r2 for storing
+ lsr r1, r2, 30 @ if either of the top two bits are set, we overflowed.
+ cbnz r1, slow_lock @ if we overflow the count go slow path
+ str r2, [r0, #LOCK_WORD_OFFSET] @ no need for strex as we hold the lock
+ bx lr
+slow_lock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case we block
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
bl artLockObjectFromCode @ (Object* obj, Thread*, SP)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_ZERO
+ DELIVER_PENDING_EXCEPTION
END art_quick_lock_object
/*
* Entry from managed code that calls artUnlockObjectFromCode and delivers exception on failure.
+ * r0 holds the possibly null object to lock.
*/
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object
+ cbz r0, slow_unlock
+ ldr r1, [r0, #LOCK_WORD_OFFSET]
+ lsr r2, r1, 30
+ cbnz r2, slow_unlock @ if either of the top two bits are set, go slow path
+ ldr r2, [r9, #THREAD_ID_OFFSET]
+ eor r3, r1, r2 @ lock_word.ThreadId() ^ self->ThreadId()
+ uxth r3, r3 @ zero top 16 bits
+ cbnz r3, slow_unlock @ do lock word and self thread id's match?
+ cmp r1, #65536
+ bpl recursive_thin_unlock
+ @ transition to unlocked, r3 holds 0
+ str r3, [r0, #LOCK_WORD_OFFSET]
+ bx lr
+recursive_thin_unlock:
+ sub r1, r1, #65536
+ str r1, [r0, #LOCK_WORD_OFFSET]
+ bx lr
+slow_unlock:
SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
mov r1, r9 @ pass Thread::Current
mov r2, sp @ pass SP
@@ -344,33 +390,96 @@ ENTRY art_quick_unlock_object
END art_quick_unlock_object
/*
- * Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
+ * Entry from managed code that calls artIsAssignableFromCode and on failure calls
+ * artThrowClassCastException.
*/
- .extern artCheckCastFromCode
+ .extern artThrowClassCastException
ENTRY art_quick_check_cast
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- bl artCheckCastFromCode @ (Class* a, Class* b, Thread*, SP)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ push {r0-r1, lr} @ save arguments, link register and pad
+ .save {r0-r1, lr}
+ .cfi_adjust_cfa_offset 12
+ .cfi_rel_offset r0, 0
+ .cfi_rel_offset r1, 4
+ .cfi_rel_offset lr, 8
+ sub sp, #4
+ .pad #4
+ .cfi_adjust_cfa_offset 4
+ bl artIsAssignableFromCode
+ cbz r0, throw_class_cast_exception
+ add sp, #4
+ .cfi_adjust_cfa_offset -4
+ pop {r0-r1, pc}
+throw_class_cast_exception:
+ add sp, #4
+ .cfi_adjust_cfa_offset -4
+ pop {r0-r1, lr}
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ b artThrowClassCastException @ (Class*, Class*, Thread*, SP)
+ bkpt
END art_quick_check_cast
/*
- * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on
- * failure.
+ * Entry from managed code for array put operations of objects where the value being stored
+ * needs to be checked for compatibility.
+ * r0 = array, r1 = index, r2 = value
*/
- .extern artCanPutArrayElementFromCode
-ENTRY art_quick_can_put_array_element
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case exception allocation triggers GC
- mov r2, r9 @ pass Thread::Current
- mov r3, sp @ pass SP
- bl artCanPutArrayElementFromCode @ (Object* element, Class* array_class, Thread*, SP)
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
-END art_quick_can_put_array_element
+ENTRY art_quick_aput_obj_with_null_and_bound_check
+ tst r0, r0
+ bne art_quick_aput_obj_with_bound_check
+ b art_quick_throw_null_pointer_exception
+END art_quick_aput_obj_with_null_and_bound_check
+
+ENTRY art_quick_aput_obj_with_bound_check
+ ldr r3, [r0, #ARRAY_LENGTH_OFFSET]
+ cmp r3, r1
+ bhi art_quick_aput_obj
+ mov r0, r1
+ mov r1, r3
+ b art_quick_throw_array_bounds
+END art_quick_aput_obj_with_bound_check
+
+ENTRY art_quick_aput_obj
+ cbz r2, do_aput_null
+ ldr r3, [r0, #CLASS_OFFSET]
+ ldr ip, [r2, #CLASS_OFFSET]
+ ldr r3, [r3, #CLASS_COMPONENT_TYPE_OFFSET]
+ cmp r3, ip @ value's type == array's component type - trivial assignability
+ bne check_assignability
+do_aput:
+ add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+ str r2, [r3, r1, lsl #2]
+ ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
+ lsr r0, r0, #7
+ strb r3, [r3, r0]
+ blx lr
+do_aput_null:
+ add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+ str r2, [r3, r1, lsl #2]
+ blx lr
+check_assignability:
+ push {r0-r2, lr} @ save arguments
+ mov r1, ip
+ mov r0, r3
+ bl artIsAssignableFromCode
+ cbz r0, throw_array_store_exception
+ pop {r0-r2, lr}
+ add r3, r0, #OBJECT_ARRAY_DATA_OFFSET
+ str r2, [r3, r1, lsl #2]
+ ldr r3, [r9, #THREAD_CARD_TABLE_OFFSET]
+ lsr r0, r0, #7
+ strb r3, [r3, r0]
+ blx lr
+throw_array_store_exception:
+ pop {r0-r2, lr}
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ mov r1, r2
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ b artThrowArrayStoreException @ (Class*, Class*, Thread*, SP)
+ bkpt @ unreached
+END art_quick_aput_obj
/*
* Entry from managed code when uninitialized static storage, this stub will run the class
@@ -706,6 +815,17 @@ ENTRY art_quick_alloc_object
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_object
+ .extern artAllocObjectFromCodeInstrumented
+ENTRY art_quick_alloc_object_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ bl artAllocObjectFromCodeInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_object_instrumented
+
/*
* Called by managed code to allocate an object when the caller doesn't know whether it has
* access to the created type.
@@ -721,6 +841,17 @@ ENTRY art_quick_alloc_object_with_access_check
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_object_with_access_check
+ .extern artAllocObjectFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_object_with_access_check_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ bl artAllocObjectFromCodeWithAccessCheckInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP)
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_object_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array.
*/
@@ -741,6 +872,23 @@ ENTRY art_quick_alloc_array
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_array
+ .extern artAllocArrayFromCodeInstrumented
+ENTRY art_quick_alloc_array_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r3, r9 @ pass Thread::Current
+ mov r12, sp
+ str r12, [sp, #-16]! @ expand the frame and pass SP
+ .pad #16
+ .cfi_adjust_cfa_offset 16
+ @ artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP)
+ bl artAllocArrayFromCodeInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array when the caller doesn't know whether it has
* access to the created type.
@@ -762,6 +910,23 @@ ENTRY art_quick_alloc_array_with_access_check
DELIVER_PENDING_EXCEPTION
END art_quick_alloc_array_with_access_check
+ .extern artAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_array_with_access_check_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r3, r9 @ pass Thread::Current
+ mov r12, sp
+ str r12, [sp, #-16]! @ expand the frame and pass SP
+ .pad #16
+ .cfi_adjust_cfa_offset 16
+ @ artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, SP)
+ bl artAllocArrayFromCodeWithAccessCheckInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -782,6 +947,23 @@ ENTRY art_quick_check_and_alloc_array
DELIVER_PENDING_EXCEPTION
END art_quick_check_and_alloc_array
+ .extern artCheckAndAllocArrayFromCodeInstrumented
+ENTRY art_quick_check_and_alloc_array_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r3, r9 @ pass Thread::Current
+ mov r12, sp
+ str r12, [sp, #-16]! @ expand the frame and pass SP
+ .pad #16
+ .cfi_adjust_cfa_offset 16
+ @ artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , SP)
+ bl artCheckAndAllocArrayFromCodeInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_check_and_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -802,6 +984,23 @@ ENTRY art_quick_check_and_alloc_array_with_access_check
DELIVER_PENDING_EXCEPTION
END art_quick_check_and_alloc_array_with_access_check
+ .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r3, r9 @ pass Thread::Current
+ mov r12, sp
+ str r12, [sp, #-16]! @ expand the frame and pass SP
+ .pad #16
+ .cfi_adjust_cfa_offset 16
+ @ artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , SP)
+ bl artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ add sp, #16 @ strip the extra frame
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ RETURN_IF_RESULT_IS_NON_ZERO
+ DELIVER_PENDING_EXCEPTION
+END art_quick_check_and_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
*/
@@ -832,15 +1031,28 @@ ENTRY art_quick_proxy_invoke_handler
mov r3, sp @ pass SP
blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP)
ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ pop frame
- .cfi_adjust_cfa_offset -48
+ add sp, #16 @ skip r1-r3, 4 bytes padding.
+ .cfi_adjust_cfa_offset -16
cbnz r2, 1f @ success if no exception is pending
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bx lr @ return on success
1:
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+ /*
+ * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+ENTRY art_quick_imt_conflict_trampoline
+ ldr r0, [sp, #0] @ load caller Method*
+ ldr r0, [r0, #METHOD_DEX_CACHE_METHODS_OFFSET] @ load dex_cache_resolved_methods
+ add r0, #OBJECT_ARRAY_DATA_OFFSET @ get starting address of data
+ ldr r0, [r0, r12, lsl 2] @ load the target method
+ b art_quick_invoke_interface_trampoline
+END art_quick_imt_conflict_trampoline
+
.extern artQuickResolutionTrampoline
ENTRY art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -850,11 +1062,7 @@ ENTRY art_quick_resolution_trampoline
cbz r0, 1f @ is code pointer null? goto exception
mov r12, r0
ldr r0, [sp, #0] @ load resolved method in r0
- ldr r1, [sp, #8] @ restore non-callee save r1
- ldrd r2, [sp, #12] @ restore non-callee saves r2-r3
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ rewind sp
- .cfi_adjust_cfa_offset -48
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
bx r12 @ tail-call into actual code
1:
RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
@@ -868,12 +1076,13 @@ ENTRY art_quick_to_interpreter_bridge
mov r2, sp @ pass SP
blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP)
ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
- ldr lr, [sp, #44] @ restore lr
- add sp, #48 @ pop frame
- .cfi_adjust_cfa_offset -48
+ add sp, #16 @ skip r1-r3, 4 bytes padding.
+ .cfi_adjust_cfa_offset -16
cbnz r2, 1f @ success if no exception is pending
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bx lr @ return on success
1:
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_to_interpreter_bridge
diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc
index ea908be22c..8c1efeb3d0 100644
--- a/runtime/arch/arm/thread_arm.cc
+++ b/runtime/arch/arm/thread_arm.cc
@@ -23,7 +23,9 @@ namespace art {
void Thread::InitCpu() {
CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_));
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
+ CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
}
} // namespace art
diff --git a/runtime/arch/mips/asm_support_mips.h b/runtime/arch/mips/asm_support_mips.h
index 9a66352ad1..530799789a 100644
--- a/runtime/arch/mips/asm_support_mips.h
+++ b/runtime/arch/mips/asm_support_mips.h
@@ -25,6 +25,8 @@
#define rSELF $s1
// Offset of field Thread::suspend_count_ verified in InitCpu
#define THREAD_FLAGS_OFFSET 0
+// Offset of field Thread::card_table_ verified in InitCpu
+#define THREAD_CARD_TABLE_OFFSET 8
// Offset of field Thread::exception_ verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 12
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 40d7cd913c..331a461354 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -41,10 +41,16 @@ extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, voi
extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t);
extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+
// Cast entrypoints.
extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
const mirror::Class* ref_class);
-extern "C" void art_quick_can_put_array_element(void*, void*);
extern "C" void art_quick_check_cast(void*, void*);
// DexCache entrypoints.
@@ -70,7 +76,10 @@ extern "C" int64_t art_quick_get64_static(uint32_t);
extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
extern "C" void* art_quick_get_obj_static(uint32_t);
-// FillArray entrypoint.
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
@@ -82,9 +91,9 @@ extern int32_t CmpgDouble(double a, double b);
extern int32_t CmplDouble(double a, double b);
extern int32_t CmpgFloat(float a, float b);
extern int32_t CmplFloat(float a, float b);
-extern "C" int64_t artLmulFromCode(int64_t a, int64_t b);
-extern "C" int64_t artLdivFromCode(int64_t a, int64_t b);
-extern "C" int64_t artLdivmodFromCode(int64_t a, int64_t b);
+extern "C" int64_t artLmul(int64_t a, int64_t b);
+extern "C" int64_t artLdiv(int64_t a, int64_t b);
+extern "C" int64_t artLmod(int64_t a, int64_t b);
// Math conversions.
extern "C" int32_t __fixsfsi(float op1); // FLOAT_TO_INT
@@ -113,10 +122,10 @@ extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t);
extern "C" int32_t art_quick_string_compareto(void*, void*);
// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
@@ -134,6 +143,30 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+static bool quick_alloc_entry_points_instrumented = false;
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented) {
+ quick_alloc_entry_points_instrumented = instrumented;
+}
+
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
+ if (quick_alloc_entry_points_instrumented) {
+ qpoints->pAllocArray = art_quick_alloc_array_instrumented;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented;
+ qpoints->pAllocObject = art_quick_alloc_object_instrumented;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented;
+ } else {
+ qpoints->pAllocArray = art_quick_alloc_array;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
+ qpoints->pAllocObject = art_quick_alloc_object;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ }
+}
+
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
// Interpreter
@@ -148,16 +181,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
// Alloc
- qpoints->pAllocArray = art_quick_alloc_array;
- qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
- qpoints->pAllocObject = art_quick_alloc_object;
- qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ ResetQuickAllocEntryPoints(qpoints);
// Cast
qpoints->pInstanceofNonTrivial = artIsAssignableFromCode;
- qpoints->pCanPutArrayElement = art_quick_can_put_array_element;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -180,7 +207,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
- // FillArray
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
@@ -209,9 +239,9 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pIdivmod = NULL;
qpoints->pD2l = art_d2l;
qpoints->pF2l = art_f2l;
- qpoints->pLdiv = artLdivFromCode;
- qpoints->pLdivmod = artLdivmodFromCode;
- qpoints->pLmul = artLmulFromCode;
+ qpoints->pLdiv = artLdiv;
+ qpoints->pLmod = artLmod;
+ qpoints->pLmul = artLmul;
qpoints->pShlLong = art_quick_shl_long;
qpoints->pShrLong = art_quick_shr_long;
qpoints->pUshrLong = art_quick_ushr_long;
@@ -223,10 +253,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pMemcpy = memcpy;
// Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3d63ccc612..451b1bb30f 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -88,15 +88,29 @@
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
- lw $gp, 52($sp)
lw $ra, 60($sp)
+ lw $s8, 56($sp)
+ lw $gp, 52($sp)
+ lw $s7, 48($sp)
+ lw $s6, 44($sp)
+ lw $s5, 40($sp)
+ lw $s4, 36($sp)
+ lw $s3, 32($sp)
+ lw $s2, 28($sp)
addiu $sp, $sp, 64
.cfi_adjust_cfa_offset -64
.endm
.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
- lw $gp, 52($sp)
lw $ra, 60($sp)
+ lw $s8, 56($sp)
+ lw $gp, 52($sp)
+ lw $s7, 48($sp)
+ lw $s6, 44($sp)
+ lw $s5, 40($sp)
+ lw $s4, 36($sp)
+ lw $s3, 32($sp)
+ lw $s2, 28($sp)
jr $ra
addiu $sp, $sp, 64
.cfi_adjust_cfa_offset -64
@@ -138,11 +152,18 @@
.endm
.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
- lw $ra, 60($sp) # restore $ra
- lw $gp, 52($sp) # restore $gp
- lw $a1, 4($sp) # restore non-callee save $a1
- lw $a2, 8($sp) # restore non-callee save $a2
- lw $a3, 12($sp) # restore non-callee save $a3
+ lw $ra, 60($sp)
+ lw $s8, 56($sp)
+ lw $gp, 52($sp)
+ lw $s7, 48($sp)
+ lw $s6, 44($sp)
+ lw $s5, 40($sp)
+ lw $s4, 36($sp)
+ lw $s3, 32($sp)
+ lw $s2, 28($sp)
+ lw $a3, 12($sp)
+ lw $a2, 8($sp)
+ lw $a1, 4($sp)
addiu $sp, $sp, 64 # pop frame
.cfi_adjust_cfa_offset -64
.endm
@@ -283,6 +304,7 @@ END art_quick_deliver_exception
.extern artThrowNullPointerExceptionFromCode
ENTRY art_quick_throw_null_pointer_exception
GENERATE_GLOBAL_POINTER
+art_quick_throw_null_pointer_exception_gp_set:
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a0, rSELF # pass Thread::Current
la $t9, artThrowNullPointerExceptionFromCode
@@ -309,6 +331,7 @@ END art_quick_throw_div_zero
.extern artThrowArrayBoundsFromCode
ENTRY art_quick_throw_array_bounds
GENERATE_GLOBAL_POINTER
+art_quick_throw_array_bounds_gp_set:
SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
move $a2, rSELF # pass Thread::Current
la $t9, artThrowArrayBoundsFromCode
@@ -481,11 +504,13 @@ END art_quick_handle_fill_data
.extern artLockObjectFromCode
ENTRY art_quick_lock_object
GENERATE_GLOBAL_POINTER
+ beqz $a0, art_quick_throw_null_pointer_exception_gp_set
+ nop
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case we block
move $a1, rSELF # pass Thread::Current
jal artLockObjectFromCode # (Object* obj, Thread*, $sp)
move $a2, $sp # pass $sp
- RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN
+ RETURN_IF_ZERO
END art_quick_lock_object
/*
@@ -494,6 +519,8 @@ END art_quick_lock_object
.extern artUnlockObjectFromCode
ENTRY art_quick_unlock_object
GENERATE_GLOBAL_POINTER
+ beqz $a0, art_quick_throw_null_pointer_exception_gp_set
+ nop
SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
move $a1, rSELF # pass Thread::Current
jal artUnlockObjectFromCode # (Object* obj, Thread*, $sp)
@@ -504,29 +531,116 @@ END art_quick_unlock_object
/*
* Entry from managed code that calls artCheckCastFromCode and delivers exception on failure.
*/
- .extern artCheckCastFromCode
+ .extern artThrowClassCastException
ENTRY art_quick_check_cast
GENERATE_GLOBAL_POINTER
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
- move $a2, rSELF # pass Thread::Current
- jal artCheckCastFromCode # (Class* a, Class* b, Thread*, $sp)
- move $a3, $sp # pass $sp
- RETURN_IF_ZERO
+ addiu $sp, $sp, -16
+ .cfi_adjust_cfa_offset 16
+ sw $ra, 12($sp)
+ .cfi_rel_offset 31, 12
+ sw $t9, 8($sp)
+ sw $a1, 4($sp)
+ sw $a0, 0($sp)
+ jal artIsAssignableFromCode
+ nop
+ beqz $v0, throw_class_cast_exception
+ lw $ra, 12($sp)
+ jr $ra
+ addiu $sp, $sp, 16
+ .cfi_adjust_cfa_offset -16
+throw_class_cast_exception:
+ lw $t9, 8($sp)
+ lw $a1, 4($sp)
+ lw $a0, 0($sp)
+ addiu $sp, $sp, 16
+ .cfi_adjust_cfa_offset -16
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ move $a2, rSELF # pass Thread::Current
+ la $t9, artThrowClassCastException
+ jr $t9 # artThrowClassCastException (Class*, Class*, Thread*, SP)
+ move $a3, $sp # pass $sp
END art_quick_check_cast
/*
- * Entry from managed code that calls artCanPutArrayElementFromCode and delivers exception on
- * failure.
+ * Entry from managed code for array put operations of objects where the value being stored
+ * needs to be checked for compatibility.
+ * a0 = array, a1 = index, a2 = value
*/
- .extern artCanPutArrayElementFromCode
-ENTRY art_quick_can_put_array_element
+ENTRY art_quick_aput_obj_with_null_and_bound_check
GENERATE_GLOBAL_POINTER
- SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case exception allocation triggers GC
- move $a2, rSELF # pass Thread::Current
- jal artCanPutArrayElementFromCode # (Object* element, Class* array_class, Thread*, $sp)
- move $a3, $sp # pass $sp
- RETURN_IF_ZERO
-END art_quick_can_put_array_element
+ bnez $a0, art_quick_aput_obj_with_bound_check_gp_set
+ nop
+ b art_quick_throw_null_pointer_exception_gp_set
+ nop
+END art_quick_aput_obj_with_null_and_bound_check
+
+ENTRY art_quick_aput_obj_with_bound_check
+ GENERATE_GLOBAL_POINTER
+art_quick_aput_obj_with_bound_check_gp_set:
+ lw $t0, ARRAY_LENGTH_OFFSET($a0)
+ sltu $t1, $a1, $t0
+ bnez $t1, art_quick_aput_obj_gp_set
+ nop
+ move $a0, $a1
+ b art_quick_throw_array_bounds_gp_set
+ move $a1, $t0
+END art_quick_aput_obj_with_bound_check
+
+ENTRY art_quick_aput_obj
+ GENERATE_GLOBAL_POINTER
+art_quick_aput_obj_gp_set:
+ beqz $a2, do_aput_null
+ nop
+ lw $t0, CLASS_OFFSET($a0)
+ lw $t1, CLASS_OFFSET($a2)
+ lw $t0, CLASS_COMPONENT_TYPE_OFFSET($t0)
+ bne $t1, $t0, check_assignability # value's type == array's component type - trivial assignability
+ nop
+do_aput:
+ sll $a1, $a1, 2
+ add $t0, $a0, $a1
+ sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
+ lw $t0, THREAD_CARD_TABLE_OFFSET(rSELF)
+ srl $t1, $a0, 7
+ add $t1, $t1, $t0
+ sb $t0, ($t1)
+ jr $ra
+ nop
+do_aput_null:
+ sll $a1, $a1, 2
+ add $t0, $a0, $a1
+ sw $a2, OBJECT_ARRAY_DATA_OFFSET($t0)
+ jr $ra
+ nop
+check_assignability:
+ addiu $sp, $sp, -32
+ .cfi_adjust_cfa_offset 32
+ sw $ra, 28($sp)
+ .cfi_rel_offset 31, 28
+ sw $t9, 12($sp)
+ sw $a2, 8($sp)
+ sw $a1, 4($sp)
+ sw $a0, 0($sp)
+ move $a1, $t1
+ move $a0, $t0
+ jal artIsAssignableFromCode # (Class*, Class*)
+ nop
+ lw $ra, 28($sp)
+ lw $t9, 12($sp)
+ lw $a2, 8($sp)
+ lw $a1, 4($sp)
+ lw $a0, 0($sp)
+ add $sp, 32
+ .cfi_adjust_cfa_offset -32
+ bnez $v0, do_aput
+ nop
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ move $a1, $a2
+ move $a2, rSELF # pass Thread::Current
+ la $t9, artThrowArrayStoreException
+ jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*, SP)
+ move $a3, $sp # pass $sp
+END art_quick_aput_obj
/*
* Entry from managed code when uninitialized static storage, this stub will run the class
@@ -770,6 +884,16 @@ ENTRY art_quick_alloc_object
RETURN_IF_NONZERO
END art_quick_alloc_object
+ .extern artAllocObjectFromCodeInstrumented
+ENTRY art_quick_alloc_object_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a2, rSELF # pass Thread::Current
+ jal artAllocObjectFromCodeInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_object_instrumented
+
/*
* Called by managed code to allocate an object when the caller doesn't know whether it has
* access to the created type.
@@ -784,6 +908,16 @@ ENTRY art_quick_alloc_object_with_access_check
RETURN_IF_NONZERO
END art_quick_alloc_object_with_access_check
+ .extern artAllocObjectFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_object_with_access_check_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a2, rSELF # pass Thread::Current
+ jal artAllocObjectFromCodeWithAccessCheckInstrumented # (uint32_t type_idx, Method* method, Thread*, $sp)
+ move $a3, $sp # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_object_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array.
*/
@@ -798,6 +932,17 @@ ENTRY art_quick_alloc_array
RETURN_IF_NONZERO
END art_quick_alloc_array
+ .extern artAllocArrayFromCodeInstrumented
+ENTRY art_quick_alloc_array_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, $sp)
+ jal artAllocArrayFromCodeInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array when the caller doesn't know whether it has
* access to the created type.
@@ -813,6 +958,17 @@ ENTRY art_quick_alloc_array_with_access_check
RETURN_IF_NONZERO
END art_quick_alloc_array_with_access_check
+ .extern artAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_alloc_array_with_access_check_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, $sp)
+ jal artAllocArrayFromCodeWithAccessCheckInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -827,6 +983,17 @@ ENTRY art_quick_check_and_alloc_array
RETURN_IF_NONZERO
END art_quick_check_and_alloc_array
+ .extern artCheckAndAllocArrayFromCodeInstrumented
+ENTRY art_quick_check_and_alloc_array_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , $sp)
+ jal artCheckAndAllocArrayFromCodeInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_check_and_alloc_array_instrumented
+
/*
* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
*/
@@ -841,6 +1008,17 @@ ENTRY art_quick_check_and_alloc_array_with_access_check
RETURN_IF_NONZERO
END art_quick_check_and_alloc_array_with_access_check
+ .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented
+ GENERATE_GLOBAL_POINTER
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC
+ move $a3, rSELF # pass Thread::Current
+ # artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , $sp)
+ jal artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented
+ sw $sp, 16($sp) # pass $sp
+ RETURN_IF_NONZERO
+END art_quick_check_and_alloc_array_with_access_check_instrumented
+
/*
* Called by managed code when the value in rSUSPEND has been decremented to 0.
*/
@@ -873,17 +1051,30 @@ ENTRY art_quick_proxy_invoke_handler
jal artQuickProxyInvokeHandler # (Method* proxy method, receiver, Thread*, SP)
move $a3, $sp # pass $sp
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
- lw $gp, 52($sp) # restore $gp
- lw $ra, 60($sp) # restore $ra
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bnez $t0, 1f
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
+ nop
jr $ra
nop
1:
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
+ /*
+ * Called to resolve an imt conflict. t0 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+ENTRY art_quick_imt_conflict_trampoline
+ GENERATE_GLOBAL_POINTER
+ lw $a0, 0($sp) # load caller Method*
+ lw $a0, METHOD_DEX_CACHE_METHODS_OFFSET($a0) # load dex_cache_resolved_methods
+ sll $t0, 2 # convert target method offset to bytes
+ add $a0, $t0 # get address of target method
+ lw $a0, OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method
+ la $t9, art_quick_invoke_interface_trampoline
+ jr $t9
+END art_quick_imt_conflict_trampoline
+
.extern artQuickResolutionTrampoline
ENTRY art_quick_resolution_trampoline
GENERATE_GLOBAL_POINTER
@@ -891,18 +1082,14 @@ ENTRY art_quick_resolution_trampoline
move $a2, rSELF # pass Thread::Current
jal artQuickResolutionTrampoline # (Method* called, receiver, Thread*, SP)
move $a3, $sp # pass $sp
- lw $gp, 52($sp) # restore $gp
- lw $ra, 60($sp) # restore $ra
beqz $v0, 1f
lw $a0, 0($sp) # load resolved method to $a0
- lw $a1, 4($sp) # restore non-callee save $a1
- lw $a2, 8($sp) # restore non-callee save $a2
- lw $a3, 12($sp) # restore non-callee save $a3
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
move $t9, $v0 # code pointer must be in $t9 to generate the global pointer
jr $v0 # tail call to method
+ nop
1:
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
DELIVER_PENDING_EXCEPTION
END art_quick_resolution_trampoline
@@ -914,11 +1101,9 @@ ENTRY art_quick_to_interpreter_bridge
jal artQuickToInterpreterBridge # (Method* method, Thread*, SP)
move $a2, $sp # pass $sp
lw $t0, THREAD_EXCEPTION_OFFSET(rSELF) # load Thread::Current()->exception_
- lw $gp, 52($sp) # restore $gp
- lw $ra, 60($sp) # restore $ra
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
bnez $t0, 1f
- addiu $sp, $sp, 64 # pop frame
- .cfi_adjust_cfa_offset -64
+ nop
jr $ra
nop
1:
diff --git a/runtime/arch/mips/thread_mips.cc b/runtime/arch/mips/thread_mips.cc
index 7364de067e..bd545497fc 100644
--- a/runtime/arch/mips/thread_mips.cc
+++ b/runtime/arch/mips/thread_mips.cc
@@ -23,6 +23,7 @@ namespace art {
void Thread::InitCpu() {
CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_));
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
}
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 1092910d78..e817ff78e6 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -21,7 +21,11 @@
// Offset of field Thread::self_ verified in InitCpu
#define THREAD_SELF_OFFSET 40
+// Offset of field Thread::card_table_ verified in InitCpu
+#define THREAD_CARD_TABLE_OFFSET 8
// Offset of field Thread::exception_ verified in InitCpu
#define THREAD_EXCEPTION_OFFSET 12
+// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu
+#define THREAD_ID_OFFSET 60
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index abc2990cc0..99b0dd548c 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -40,10 +40,16 @@ extern "C" void* art_quick_alloc_object_with_access_check(uint32_t type_idx, voi
extern "C" void* art_quick_check_and_alloc_array(uint32_t, void*, int32_t);
extern "C" void* art_quick_check_and_alloc_array_with_access_check(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_alloc_object_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_alloc_object_with_access_check_instrumented(uint32_t type_idx, void* method);
+extern "C" void* art_quick_check_and_alloc_array_instrumented(uint32_t, void*, int32_t);
+extern "C" void* art_quick_check_and_alloc_array_with_access_check_instrumented(uint32_t, void*, int32_t);
+
// Cast entrypoints.
extern "C" uint32_t art_quick_is_assignable(const mirror::Class* klass,
const mirror::Class* ref_class);
-extern "C" void art_quick_can_put_array_element(void*, void*);
extern "C" void art_quick_check_cast(void*, void*);
// DexCache entrypoints.
@@ -66,7 +72,10 @@ extern "C" int64_t art_quick_get64_static(uint32_t);
extern "C" void* art_quick_get_obj_instance(uint32_t, void*);
extern "C" void* art_quick_get_obj_static(uint32_t);
-// FillArray entrypoint.
+// Array entrypoints.
+extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*);
+extern "C" void art_quick_aput_obj(void*, uint32_t, void*);
extern "C" void art_quick_handle_fill_data(void*, void*);
// Lock entrypoints.
@@ -82,7 +91,7 @@ extern "C" int64_t art_quick_d2l(double);
extern "C" int64_t art_quick_f2l(float);
extern "C" int32_t art_quick_idivmod(int32_t, int32_t);
extern "C" int64_t art_quick_ldiv(int64_t, int64_t);
-extern "C" int64_t art_quick_ldivmod(int64_t, int64_t);
+extern "C" int64_t art_quick_lmod(int64_t, int64_t);
extern "C" int64_t art_quick_lmul(int64_t, int64_t);
extern "C" uint64_t art_quick_lshl(uint64_t, uint32_t);
extern "C" uint64_t art_quick_lshr(uint64_t, uint32_t);
@@ -95,10 +104,10 @@ extern "C" int32_t art_quick_string_compareto(void*, void*);
extern "C" void* art_quick_memcpy(void*, const void*, size_t);
// Invoke entrypoints.
+extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*);
extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*);
extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*);
-extern "C" void art_quick_invoke_interface_trampoline(uint32_t, void*);
extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*);
extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*);
@@ -116,6 +125,30 @@ extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+static bool quick_alloc_entry_points_instrumented = false;
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented) {
+ quick_alloc_entry_points_instrumented = instrumented;
+}
+
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints) {
+ if (quick_alloc_entry_points_instrumented) {
+ qpoints->pAllocArray = art_quick_alloc_array_instrumented;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check_instrumented;
+ qpoints->pAllocObject = art_quick_alloc_object_instrumented;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check_instrumented;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array_instrumented;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check_instrumented;
+ } else {
+ qpoints->pAllocArray = art_quick_alloc_array;
+ qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
+ qpoints->pAllocObject = art_quick_alloc_object;
+ qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
+ qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
+ qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ }
+}
+
void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) {
// Interpreter
@@ -130,16 +163,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge;
// Alloc
- qpoints->pAllocArray = art_quick_alloc_array;
- qpoints->pAllocArrayWithAccessCheck = art_quick_alloc_array_with_access_check;
- qpoints->pAllocObject = art_quick_alloc_object;
- qpoints->pAllocObjectWithAccessCheck = art_quick_alloc_object_with_access_check;
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array;
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check;
+ ResetQuickAllocEntryPoints(qpoints);
// Cast
qpoints->pInstanceofNonTrivial = art_quick_is_assignable;
- qpoints->pCanPutArrayElement = art_quick_can_put_array_element;
qpoints->pCheckCast = art_quick_check_cast;
// DexCache
@@ -162,7 +189,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pGet64Static = art_quick_get64_static;
qpoints->pGetObjStatic = art_quick_get_obj_static;
- // FillArray
+ // Array
+ qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
+ qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
+ qpoints->pAputObject = art_quick_aput_obj;
qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
@@ -192,7 +222,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pD2l = art_quick_d2l;
qpoints->pF2l = art_quick_f2l;
qpoints->pLdiv = art_quick_ldiv;
- qpoints->pLdivmod = art_quick_ldivmod;
+ qpoints->pLmod = art_quick_lmod;
qpoints->pLmul = art_quick_lmul;
qpoints->pShlLong = art_quick_lshl;
qpoints->pShrLong = art_quick_lshr;
@@ -205,10 +235,10 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints,
qpoints->pMemcpy = art_quick_memcpy;
// Invocation
+ qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline;
qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline;
qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge;
qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check;
- qpoints->pInvokeInterfaceTrampoline = art_quick_invoke_interface_trampoline;
qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check;
qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check;
qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check;
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index dbf552faaf..6fe499374a 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -41,7 +41,10 @@ MACRO0(SETUP_REF_ONLY_CALLEE_SAVE_FRAME)
END_MACRO
MACRO0(RESTORE_REF_ONLY_CALLEE_SAVE_FRAME)
- addl MACRO_LITERAL(28), %esp // Unwind stack up to return address
+ addl MACRO_LITERAL(16), %esp // Unwind stack up to return address
+ POP ebp // Restore callee saves (ebx is saved/restored by the upcall)
+ POP esi
+ POP edi
.cfi_adjust_cfa_offset -28
END_MACRO
@@ -389,26 +392,207 @@ THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCod
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO
THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO
+TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO
+THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO
+
TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_EAX_NOT_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_EAX_NOT_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO
TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO
-ONE_ARG_DOWNCALL art_quick_lock_object, artLockObjectFromCode, ret
-ONE_ARG_DOWNCALL art_quick_unlock_object, artUnlockObjectFromCode, RETURN_IF_EAX_ZERO
-
TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
+DEFINE_FUNCTION art_quick_lock_object
+ testl %eax, %eax // null check object/eax
+ jz slow_lock
+retry_lock:
+ movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word
+ test LITERAL(0xC0000000), %ecx // test the 2 high bits.
+ jne slow_lock // slow path if either of the two high bits are set.
+ movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id
+ test %ecx, %ecx
+ jnz already_thin // lock word contains a thin lock
+ // unlocked case - %edx holds thread id with count of 0
+ movl %eax, %ecx // remember object in case of retry
+ xor %eax, %eax // eax == 0 for comparison with lock word in cmpxchg
+ lock cmpxchg %edx, LOCK_WORD_OFFSET(%ecx)
+ jnz cmpxchg_fail // cmpxchg failed retry
+ ret
+cmpxchg_fail:
+ movl %ecx, %eax // restore eax
+ jmp retry_lock
+already_thin:
+ cmpw %ax, %dx // do we hold the lock already?
+ jne slow_lock
+ addl LITERAL(65536), %eax // increment recursion count
+ test LITERAL(0xC0000000), %eax // overflowed if either of top two bits are set
+ jne slow_lock // count overflowed so go slow
+ movl %eax, LOCK_WORD_OFFSET(%ecx) // update lockword, cmpxchg not necessary as we hold lock
+ ret
+slow_lock:
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %edx // remember SP
+ // Outgoing argument set up
+ PUSH eax // push padding
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH eax // pass object
+ call artLockObjectFromCode // artLockObjectFromCode(object, Thread*, SP)
+ addl MACRO_LITERAL(16), %esp // pop arguments
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_lock_object
+
+DEFINE_FUNCTION art_quick_unlock_object
+ testl %eax, %eax // null check object/eax
+ jz slow_unlock
+ movl LOCK_WORD_OFFSET(%eax), %ecx // ecx := lock word
+ movl %fs:THREAD_ID_OFFSET, %edx // edx := thread id
+ test %ecx, %ecx
+ jb slow_unlock // lock word contains a monitor
+ cmpw %cx, %dx // does the thread id match?
+ jne slow_unlock
+ cmpl LITERAL(65536), %ecx
+ jae recursive_thin_unlock
+ movl LITERAL(0), LOCK_WORD_OFFSET(%eax)
+ ret
+recursive_thin_unlock:
+ subl LITERAL(65536), %ecx
+ mov %ecx, LOCK_WORD_OFFSET(%eax)
+ ret
+slow_unlock:
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME // save ref containing registers for GC
+ mov %esp, %edx // remember SP
+ // Outgoing argument set up
+ PUSH eax // push padding
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH eax // pass object
+ call artUnlockObjectFromCode // artUnlockObjectFromCode(object, Thread*, SP)
+ addl MACRO_LITERAL(16), %esp // pop arguments
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
+ RETURN_IF_EAX_ZERO
+END_FUNCTION art_quick_unlock_object
+
DEFINE_FUNCTION art_quick_is_assignable
PUSH eax // alignment padding
- PUSH ecx // pass arg2
- PUSH eax // pass arg1
- call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b, Thread*, SP)
+ PUSH ecx // pass arg2 - obj->klass
+ PUSH eax // pass arg1 - checked class
+ call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass)
addl LITERAL(12), %esp // pop arguments
.cfi_adjust_cfa_offset -12
ret
END_FUNCTION art_quick_is_assignable
+DEFINE_FUNCTION art_quick_check_cast
+ PUSH eax // alignment padding
+ PUSH ecx // pass arg2 - obj->klass
+ PUSH eax // pass arg1 - checked class
+ call SYMBOL(artIsAssignableFromCode) // (Class* klass, Class* ref_klass)
+ testl %eax, %eax
+ jz 1f // jump forward if not assignable
+ addl LITERAL(12), %esp // pop arguments
+ .cfi_adjust_cfa_offset -12
+ ret
+1:
+ POP eax // pop arguments
+ POP ecx
+ addl LITERAL(4), %esp
+ .cfi_adjust_cfa_offset -12
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov %esp, %edx
+ // Outgoing argument set up
+ PUSH edx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH ecx // pass arg2
+ PUSH eax // pass arg1
+ call SYMBOL(artThrowClassCastException) // (Class* a, Class* b, Thread*, SP)
+ int3 // unreached
+END_FUNCTION art_quick_check_cast
+
+ /*
+ * Entry from managed code for array put operations of objects where the value being stored
+ * needs to be checked for compatibility.
+ * eax = array, ecx = index, edx = value
+ */
+DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
+ testl %eax, %eax
+ jnz art_quick_aput_obj_with_bound_check
+ jmp art_quick_throw_null_pointer_exception
+END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
+
+DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
+ movl ARRAY_LENGTH_OFFSET(%eax), %ebx
+ cmpl %ebx, %ecx
+ jb art_quick_aput_obj
+ mov %ecx, %eax
+ mov %ebx, %ecx
+ jmp art_quick_throw_array_bounds
+END_FUNCTION art_quick_aput_obj_with_bound_check
+
+DEFINE_FUNCTION art_quick_aput_obj
+ test %edx, %edx // store of null
+ jz do_aput_null
+ movl CLASS_OFFSET(%eax), %ebx
+ movl CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ cmpl CLASS_OFFSET(%edx), %ebx // value's type == array's component type - trivial assignability
+ jne check_assignability
+do_aput:
+ movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
+ movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
+ shrl LITERAL(7), %eax
+ movb %dl, (%edx, %eax)
+ ret
+do_aput_null:
+ movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
+ ret
+check_assignability:
+ PUSH eax // save arguments
+ PUSH ecx
+ PUSH edx
+ subl LITERAL(8), %esp // alignment padding
+ .cfi_adjust_cfa_offset 8
+ pushl CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored
+ .cfi_adjust_cfa_offset 4
+ PUSH ebx // pass arg1 - component type of the array
+ call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
+ addl LITERAL(16), %esp // pop arguments
+ .cfi_adjust_cfa_offset -16
+ testl %eax, %eax
+ jz throw_array_store_exception
+ POP edx
+ POP ecx
+ POP eax
+ movl %edx, OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput
+ movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
+ shrl LITERAL(7), %eax
+ movb %dl, (%edx, %eax)
+ ret
+throw_array_store_exception:
+ POP edx
+ POP ecx
+ POP eax
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context
+ mov %esp, %ecx
+ // Outgoing argument set up
+ PUSH ecx // pass SP
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ .cfi_adjust_cfa_offset 4
+ PUSH edx // pass arg2 - value
+ PUSH eax // pass arg1 - array
+ call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*, SP)
+ int3 // unreached
+END_FUNCTION art_quick_aput_obj
+
DEFINE_FUNCTION art_quick_memcpy
PUSH edx // pass arg3
PUSH ecx // pass arg2
@@ -419,9 +603,6 @@ DEFINE_FUNCTION art_quick_memcpy
ret
END_FUNCTION art_quick_memcpy
-TWO_ARG_DOWNCALL art_quick_check_cast, artCheckCastFromCode, RETURN_IF_EAX_ZERO
-TWO_ARG_DOWNCALL art_quick_can_put_array_element, artCanPutArrayElementFromCode, RETURN_IF_EAX_ZERO
-
NO_ARG_DOWNCALL art_quick_test_suspend, artTestSuspendFromCode, ret
DEFINE_FUNCTION art_quick_fmod
@@ -508,30 +689,30 @@ check_arg2:
END_FUNCTION art_quick_idivmod
DEFINE_FUNCTION art_quick_ldiv
- subl LITERAL(12), %esp // alignment padding
+ subl LITERAL(12), %esp // alignment padding
.cfi_adjust_cfa_offset 12
PUSH ebx // pass arg4 b.hi
PUSH edx // pass arg3 b.lo
PUSH ecx // pass arg2 a.hi
- PUSH eax // pass arg1 a.lo
- call SYMBOL(artLdivFromCode) // (jlong a, jlong b)
- addl LITERAL(28), %esp // pop arguments
+ PUSH eax // pass arg1 a.lo
+ call SYMBOL(artLdiv) // (jlong a, jlong b)
+ addl LITERAL(28), %esp // pop arguments
.cfi_adjust_cfa_offset -28
ret
END_FUNCTION art_quick_ldiv
-DEFINE_FUNCTION art_quick_ldivmod
- subl LITERAL(12), %esp // alignment padding
+DEFINE_FUNCTION art_quick_lmod
+ subl LITERAL(12), %esp // alignment padding
.cfi_adjust_cfa_offset 12
PUSH ebx // pass arg4 b.hi
PUSH edx // pass arg3 b.lo
PUSH ecx // pass arg2 a.hi
- PUSH eax // pass arg1 a.lo
- call SYMBOL(artLdivmodFromCode) // (jlong a, jlong b)
- addl LITERAL(28), %esp // pop arguments
+ PUSH eax // pass arg1 a.lo
+ call SYMBOL(artLmod) // (jlong a, jlong b)
+ addl LITERAL(28), %esp // pop arguments
.cfi_adjust_cfa_offset -28
ret
-END_FUNCTION art_quick_ldivmod
+END_FUNCTION art_quick_lmod
DEFINE_FUNCTION art_quick_lmul
imul %eax, %ebx // ebx = a.lo(eax) * b.hi(ebx)
@@ -818,6 +999,20 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_proxy_invoke_handler
+ /*
+ * Called to resolve an imt conflict. xmm0 is a hidden argument that holds the target method's
+ * dex method index.
+ */
+DEFINE_FUNCTION art_quick_imt_conflict_trampoline
+ PUSH ecx
+ movl 8(%esp), %eax // load caller Method*
+ movl METHOD_DEX_CACHE_METHODS_OFFSET(%eax), %eax // load dex_cache_resolved_methods
+ movd %xmm0, %ecx // get target method index stored in xmm0
+ movl OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4), %eax // load the target method
+ POP ecx
+ jmp art_quick_invoke_interface_trampoline
+END_FUNCTION art_quick_imt_conflict_trampoline
+
DEFINE_FUNCTION art_quick_resolution_trampoline
SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
PUSH esp // pass SP
@@ -855,8 +1050,9 @@ DEFINE_FUNCTION art_quick_to_interpreter_bridge
movd %eax, %xmm0 // place return value also into floating point return value
movd %edx, %xmm1
punpckldq %xmm1, %xmm0
- addl LITERAL(44), %esp // pop arguments
- .cfi_adjust_cfa_offset -44
+ addl LITERAL(16), %esp // pop arguments
+ .cfi_adjust_cfa_offset -16
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception
END_FUNCTION art_quick_to_interpreter_bridge
diff --git a/runtime/arch/x86/thread_x86.cc b/runtime/arch/x86/thread_x86.cc
index dd3e7dd137..42789cb1cd 100644
--- a/runtime/arch/x86/thread_x86.cc
+++ b/runtime/arch/x86/thread_x86.cc
@@ -134,6 +134,8 @@ void Thread::InitCpu() {
// Sanity check other offsets.
CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_));
+ CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_));
+ CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
}
} // namespace art
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index aca93a5552..e9bbf91761 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -21,13 +21,25 @@
// check.
#define SUSPEND_CHECK_INTERVAL (1000)
+// Offsets within java.lang.Object.
+#define CLASS_OFFSET 0
+#define LOCK_WORD_OFFSET 4
+
+// Offsets within java.lang.Class.
+#define CLASS_COMPONENT_TYPE_OFFSET 12
+
+// Array offsets.
+#define ARRAY_LENGTH_OFFSET 8
+#define OBJECT_ARRAY_DATA_OFFSET 12
+
// Offsets within java.lang.String.
#define STRING_VALUE_OFFSET 8
#define STRING_COUNT_OFFSET 12
#define STRING_OFFSET_OFFSET 20
#define STRING_DATA_OFFSET 12
-// Offset of field Method::entry_point_from_compiled_code_
+// Offsets within java.lang.Method.
+#define METHOD_DEX_CACHE_METHODS_OFFSET 16
#define METHOD_CODE_OFFSET 40
#endif // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc
new file mode 100644
index 0000000000..4f7753d476
--- /dev/null
+++ b/runtime/base/allocator.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013 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 "allocator.h"
+
+#include <inttypes.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+
+namespace art {
+
+class MallocAllocator : public Allocator {
+ public:
+ explicit MallocAllocator() {}
+ ~MallocAllocator() {}
+
+ virtual void* Alloc(size_t size) {
+ return calloc(sizeof(uint8_t), size);
+ }
+
+ virtual void Free(void* p) {
+ free(p);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MallocAllocator);
+};
+
+MallocAllocator g_malloc_allocator;
+
+class NoopAllocator : public Allocator {
+ public:
+ explicit NoopAllocator() {}
+ ~NoopAllocator() {}
+
+ virtual void* Alloc(size_t size) {
+ LOG(FATAL) << "NoopAllocator::Alloc should not be called";
+ return NULL;
+ }
+
+ virtual void Free(void* p) {
+ // Noop.
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoopAllocator);
+};
+
+NoopAllocator g_noop_allocator;
+
+Allocator* Allocator::GetMallocAllocator() {
+ return &g_malloc_allocator;
+}
+
+Allocator* Allocator::GetNoopAllocator() {
+ return &g_noop_allocator;
+}
+
+
+} // namespace art
diff --git a/compiler/llvm/runtime_support_builder_thumb2.h b/runtime/base/allocator.h
index c47a2744ef..917bf0b6e3 100644
--- a/compiler/llvm/runtime_support_builder_thumb2.h
+++ b/runtime/base/allocator.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -14,24 +14,28 @@
* limitations under the License.
*/
-#ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
-#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
+#ifndef ART_RUNTIME_BASE_ALLOCATOR_H_
+#define ART_RUNTIME_BASE_ALLOCATOR_H_
-#include "runtime_support_builder_arm.h"
+#include "base/macros.h"
namespace art {
-namespace llvm {
-class RuntimeSupportBuilderThumb2 : public RuntimeSupportBuilderARM {
+class Allocator {
public:
- RuntimeSupportBuilderThumb2(::llvm::LLVMContext& context, ::llvm::Module& module, IRBuilder& irb)
- : RuntimeSupportBuilderARM(context, module, irb) {}
+ static Allocator* GetMallocAllocator();
+ static Allocator* GetNoopAllocator();
- /* Monitor */
- virtual void EmitLockObject(::llvm::Value* object);
+ Allocator() {}
+ virtual ~Allocator() {}
+
+ virtual void* Alloc(size_t) = 0;
+ virtual void Free(void*) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Allocator);
};
-} // namespace llvm
} // namespace art
-#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_BUILDER_THUMB2_H_
+#endif // ART_RUNTIME_BASE_ALLOCATOR_H_
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
new file mode 100644
index 0000000000..3b82651498
--- /dev/null
+++ b/runtime/base/bit_vector.cc
@@ -0,0 +1,177 @@
+/*
+ * 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 "bit_vector.h"
+
+namespace art {
+
+// TODO: profile to make sure this is still a win relative to just using shifted masks.
+static uint32_t check_masks[32] = {
+ 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010,
+ 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200,
+ 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000,
+ 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000,
+ 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
+ 0x40000000, 0x80000000 };
+
+static inline uint32_t BitsToWords(uint32_t bits) {
+ return (bits + 31) >> 5;
+}
+
+// TODO: replace excessive argument defaulting when we are at gcc 4.7
+// or later on host with delegating constructor support. Specifically,
+// starts_bits and storage_size/storage are mutually exclusive.
+BitVector::BitVector(uint32_t start_bits,
+ bool expandable,
+ Allocator* allocator,
+ uint32_t storage_size,
+ uint32_t* storage)
+ : allocator_(allocator),
+ expandable_(expandable),
+ storage_size_(storage_size),
+ storage_(storage) {
+ DCHECK_EQ(sizeof(storage_[0]), 4U); // Assuming 32-bit units.
+ if (storage_ == NULL) {
+ storage_size_ = BitsToWords(start_bits);
+ storage_ = static_cast<uint32_t*>(allocator_->Alloc(storage_size_ * sizeof(uint32_t)));
+ }
+}
+
+BitVector::~BitVector() {
+ allocator_->Free(storage_);
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool BitVector::IsBitSet(uint32_t num) const {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+
+ uint32_t val = storage_[num >> 5] & check_masks[num & 0x1f];
+ return (val != 0);
+}
+
+// Mark all bits bit as "clear".
+void BitVector::ClearAllBits() {
+ memset(storage_, 0, storage_size_ * sizeof(uint32_t));
+}
+
+// Mark the specified bit as "set".
+/*
+ * TUNING: this could have pathologically bad growth/expand behavior. Make sure we're
+ * not using it badly or change resize mechanism.
+ */
+void BitVector::SetBit(uint32_t num) {
+ if (num >= storage_size_ * sizeof(uint32_t) * 8) {
+ DCHECK(expandable_) << "Attempted to expand a non-expandable bitmap to position " << num;
+
+ /* Round up to word boundaries for "num+1" bits */
+ uint32_t new_size = BitsToWords(num + 1);
+ DCHECK_GT(new_size, storage_size_);
+ uint32_t *new_storage =
+ static_cast<uint32_t*>(allocator_->Alloc(new_size * sizeof(uint32_t)));
+ memcpy(new_storage, storage_, storage_size_ * sizeof(uint32_t));
+ // Zero out the new storage words.
+ memset(&new_storage[storage_size_], 0, (new_size - storage_size_) * sizeof(uint32_t));
+ // TOTO: collect stats on space wasted because of resize.
+ storage_ = new_storage;
+ storage_size_ = new_size;
+ }
+
+ storage_[num >> 5] |= check_masks[num & 0x1f];
+}
+
+// Mark the specified bit as "unset".
+void BitVector::ClearBit(uint32_t num) {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+ storage_[num >> 5] &= ~check_masks[num & 0x1f];
+}
+
+// Intersect with another bit vector. Sizes and expandability must be the same.
+void BitVector::Intersect(const BitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ DCHECK_EQ(expandable_, src->IsExpandable());
+ for (uint32_t idx = 0; idx < storage_size_; idx++) {
+ storage_[idx] &= src->GetRawStorageWord(idx);
+ }
+}
+
+/*
+ * Union with another bit vector. Sizes and expandability must be the same.
+ */
+void BitVector::Union(const BitVector* src) {
+ DCHECK_EQ(storage_size_, src->GetStorageSize());
+ DCHECK_EQ(expandable_, src->IsExpandable());
+ for (uint32_t idx = 0; idx < storage_size_; idx++) {
+ storage_[idx] |= src->GetRawStorageWord(idx);
+ }
+}
+
+// Count the number of bits that are set.
+uint32_t BitVector::NumSetBits() const {
+ uint32_t count = 0;
+ for (uint32_t word = 0; word < storage_size_; word++) {
+ count += __builtin_popcount(storage_[word]);
+ }
+ return count;
+}
+
+// Count the number of bits that are set up through and including num.
+uint32_t BitVector::NumSetBits(uint32_t num) const {
+ DCHECK_LT(num, storage_size_ * sizeof(uint32_t) * 8);
+ uint32_t last_word = num >> 5;
+ uint32_t partial_word_bits = num & 0x1f;
+
+ // partial_word_bits | # | | | partial_word_mask
+ // 00000 | 0 | 0xffffffff >> (31 - 0) | (1 << (0 + 1)) - 1 | 0x00000001
+ // 00001 | 1 | 0xffffffff >> (31 - 1) | (1 << (1 + 1)) - 1 | 0x00000003
+ // 00010 | 2 | 0xffffffff >> (31 - 2) | (1 << (2 + 1)) - 1 | 0x00000007
+ // ..... |
+ // 11110 | 30 | 0xffffffff >> (31 - 30) | (1 << (30 + 1)) - 1 | 0x7fffffff
+ // 11111 | 31 | 0xffffffff >> (31 - 31) | last_full_word++ | 0xffffffff
+ uint32_t partial_word_mask = 0xffffffff >> (0x1f - partial_word_bits);
+
+ uint32_t count = 0;
+ for (uint32_t word = 0; word < last_word; word++) {
+ count += __builtin_popcount(storage_[word]);
+ }
+ count += __builtin_popcount(storage_[last_word] & partial_word_mask);
+ return count;
+}
+
+BitVector::Iterator* BitVector::GetIterator() const {
+ return new (allocator_) Iterator(this);
+}
+
+/*
+ * Mark specified number of bits as "set". Cannot set all bits like ClearAll
+ * since there might be unused bits - setting those to one will confuse the
+ * iterator.
+ */
+void BitVector::SetInitialBits(uint32_t num_bits) {
+ DCHECK_LE(BitsToWords(num_bits), storage_size_);
+ uint32_t idx;
+ for (idx = 0; idx < (num_bits >> 5); idx++) {
+ storage_[idx] = -1;
+ }
+ uint32_t rem_num_bits = num_bits & 0x1f;
+ if (rem_num_bits) {
+ storage_[idx] = (1 << rem_num_bits) - 1;
+ }
+}
+
+} // namespace art
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
new file mode 100644
index 0000000000..74bec087a3
--- /dev/null
+++ b/runtime/base/bit_vector.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 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_BASE_BIT_VECTOR_H_
+#define ART_RUNTIME_BASE_BIT_VECTOR_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "allocator.h"
+#include "base/logging.h"
+#include "utils.h"
+
+namespace art {
+
+/*
+ * Expanding bitmap, used for tracking resources. Bits are numbered starting
+ * from zero. All operations on a BitVector are unsynchronized.
+ */
+class BitVector {
+ public:
+ class Iterator {
+ public:
+ explicit Iterator(const BitVector* bit_vector)
+ : p_bits_(bit_vector),
+ bit_storage_(bit_vector->GetRawStorage()),
+ bit_index_(0),
+ bit_size_(p_bits_->storage_size_ * sizeof(uint32_t) * 8) {}
+
+ // Return the position of the next set bit. -1 means end-of-element reached.
+ int32_t Next() {
+ // Did anything obviously change since we started?
+ DCHECK_EQ(bit_size_, p_bits_->GetStorageSize() * sizeof(uint32_t) * 8);
+ DCHECK_EQ(bit_storage_, p_bits_->GetRawStorage());
+
+ if (UNLIKELY(bit_index_ >= bit_size_)) return -1;
+
+ uint32_t word_index = bit_index_ / 32;
+ uint32_t word = bit_storage_[word_index];
+ // Mask out any bits in the first word we've already considered.
+ word >>= bit_index_ & 0x1f;
+ if (word == 0) {
+ bit_index_ &= ~0x1f;
+ do {
+ word_index++;
+ if (UNLIKELY((word_index * 32) >= bit_size_)) {
+ bit_index_ = bit_size_;
+ return -1;
+ }
+ word = bit_storage_[word_index];
+ bit_index_ += 32;
+ } while (word == 0);
+ }
+ bit_index_ += CTZ(word) + 1;
+ return bit_index_ - 1;
+ }
+
+ static void* operator new(size_t size, Allocator* allocator) {
+ return allocator->Alloc(sizeof(BitVector::Iterator));
+ };
+ static void operator delete(void* p) {
+ Iterator* it = reinterpret_cast<Iterator*>(p);
+ it->p_bits_->allocator_->Free(p);
+ }
+
+ private:
+ const BitVector* const p_bits_;
+ const uint32_t* const bit_storage_;
+ uint32_t bit_index_; // Current index (size in bits).
+ const uint32_t bit_size_; // Size of vector in bits.
+
+ friend class BitVector;
+ };
+
+ BitVector(uint32_t start_bits,
+ bool expandable,
+ Allocator* allocator,
+ uint32_t storage_size = 0,
+ uint32_t* storage = NULL);
+
+ virtual ~BitVector();
+
+ void SetBit(uint32_t num);
+ void ClearBit(uint32_t num);
+ bool IsBitSet(uint32_t num) const;
+ void ClearAllBits();
+ void SetInitialBits(uint32_t num_bits);
+ void Copy(BitVector* src) {
+ memcpy(storage_, src->GetRawStorage(), sizeof(uint32_t) * storage_size_);
+ }
+ void Intersect(const BitVector* src2);
+ void Union(const BitVector* src);
+ // Are we equal to another bit vector? Note: expandability attributes must also match.
+ bool Equal(const BitVector* src) {
+ return (storage_size_ == src->GetStorageSize()) &&
+ (expandable_ == src->IsExpandable()) &&
+ (memcmp(storage_, src->GetRawStorage(), storage_size_ * sizeof(uint32_t)) == 0);
+ }
+ uint32_t NumSetBits() const;
+ uint32_t NumSetBits(uint32_t num) const;
+
+ Iterator* GetIterator() const;
+
+ uint32_t GetStorageSize() const { return storage_size_; }
+ bool IsExpandable() const { return expandable_; }
+ uint32_t GetRawStorageWord(size_t idx) const { return storage_[idx]; }
+ uint32_t* GetRawStorage() { return storage_; }
+ const uint32_t* GetRawStorage() const { return storage_; }
+ size_t GetSizeOf() const { return storage_size_ * sizeof(uint32_t); }
+
+ private:
+ Allocator* const allocator_;
+ const bool expandable_; // expand bitmap if we run out?
+ uint32_t storage_size_; // current size, in 32-bit words.
+ uint32_t* storage_;
+};
+
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_VECTOR_H_
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
new file mode 100644
index 0000000000..d99d059e0f
--- /dev/null
+++ b/runtime/base/bit_vector_test.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2013 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 "UniquePtr.h"
+#include "bit_vector.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(BitVector, Test) {
+ const size_t kBits = 32;
+
+ BitVector bv(kBits, false, Allocator::GetMallocAllocator());
+ EXPECT_EQ(1U, bv.GetStorageSize());
+ EXPECT_EQ(kWordSize, bv.GetSizeOf());
+ EXPECT_FALSE(bv.IsExpandable());
+
+ EXPECT_EQ(0U, bv.NumSetBits());
+ EXPECT_EQ(0U, bv.NumSetBits(0));
+ EXPECT_EQ(0U, bv.NumSetBits(kBits - 1));
+ for (size_t i = 0; i < kBits; i++) {
+ EXPECT_FALSE(bv.IsBitSet(i));
+ }
+ EXPECT_EQ(0U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0U, *bv.GetRawStorage());
+
+ BitVector::Iterator empty_iterator(&bv);
+ EXPECT_EQ(-1, empty_iterator.Next());
+
+ UniquePtr<BitVector::Iterator> empty_iterator_on_heap(bv.GetIterator());
+ EXPECT_EQ(-1, empty_iterator_on_heap->Next());
+
+ bv.SetBit(0);
+ bv.SetBit(kBits - 1);
+ EXPECT_EQ(2U, bv.NumSetBits());
+ EXPECT_EQ(1U, bv.NumSetBits(0));
+ EXPECT_EQ(2U, bv.NumSetBits(kBits - 1));
+ EXPECT_TRUE(bv.IsBitSet(0));
+ for (size_t i = 1; i < kBits - 1; i++) {
+ EXPECT_FALSE(bv.IsBitSet(i));
+ }
+ EXPECT_TRUE(bv.IsBitSet(kBits - 1));
+ EXPECT_EQ(0x80000001U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x80000001U, *bv.GetRawStorage());
+
+ BitVector::Iterator iterator(&bv);
+ EXPECT_EQ(0, iterator.Next());
+ EXPECT_EQ(static_cast<int>(kBits - 1), iterator.Next());
+ EXPECT_EQ(-1, iterator.Next());
+}
+
+TEST(BitVector, NoopAllocator) {
+ const uint32_t kWords = 2;
+
+ uint32_t bits[kWords];
+ memset(bits, 0, sizeof(bits));
+
+ BitVector bv(0U, false, Allocator::GetNoopAllocator(), kWords, bits);
+ EXPECT_EQ(kWords, bv.GetStorageSize());
+ EXPECT_EQ(kWords * kWordSize, bv.GetSizeOf());
+ EXPECT_EQ(bits, bv.GetRawStorage());
+ EXPECT_EQ(0U, bv.NumSetBits());
+
+ bv.SetBit(8);
+ EXPECT_EQ(1U, bv.NumSetBits());
+ EXPECT_EQ(0x00000100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(1U, bv.NumSetBits());
+
+ bv.SetBit(16);
+ EXPECT_EQ(2U, bv.NumSetBits());
+ EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00000000U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(2U, bv.NumSetBits());
+
+ bv.SetBit(32);
+ EXPECT_EQ(3U, bv.NumSetBits());
+ EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00000001U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(3U, bv.NumSetBits());
+
+ bv.SetBit(48);
+ EXPECT_EQ(4U, bv.NumSetBits());
+ EXPECT_EQ(0x00010100U, bv.GetRawStorageWord(0));
+ EXPECT_EQ(0x00010001U, bv.GetRawStorageWord(1));
+ EXPECT_EQ(4U, bv.NumSetBits());
+
+ EXPECT_EQ(0U, bv.NumSetBits(0));
+
+ EXPECT_EQ(0U, bv.NumSetBits(7));
+ EXPECT_EQ(1U, bv.NumSetBits(8));
+ EXPECT_EQ(1U, bv.NumSetBits(9));
+
+ EXPECT_EQ(1U, bv.NumSetBits(15));
+ EXPECT_EQ(2U, bv.NumSetBits(16));
+ EXPECT_EQ(2U, bv.NumSetBits(17));
+
+ EXPECT_EQ(2U, bv.NumSetBits(31));
+ EXPECT_EQ(3U, bv.NumSetBits(32));
+ EXPECT_EQ(3U, bv.NumSetBits(33));
+
+ EXPECT_EQ(3U, bv.NumSetBits(47));
+ EXPECT_EQ(4U, bv.NumSetBits(48));
+ EXPECT_EQ(4U, bv.NumSetBits(49));
+
+ EXPECT_EQ(4U, bv.NumSetBits(63));
+}
+
+} // namespace art
diff --git a/runtime/base/macros.h b/runtime/base/macros.h
index 6531858f9d..00a530a206 100644
--- a/runtime/base/macros.h
+++ b/runtime/base/macros.h
@@ -130,6 +130,10 @@ char (&ArraySizeHelper(T (&array)[N]))[N];
#define LIKELY(x) __builtin_expect((x), true)
#define UNLIKELY(x) __builtin_expect((x), false)
+// Stringify the argument.
+#define QUOTE(x) #x
+#define STRINGIFY(x) QUOTE(x)
+
#ifndef NDEBUG
#define ALWAYS_INLINE
#else
@@ -138,8 +142,10 @@ char (&ArraySizeHelper(T (&array)[N]))[N];
#if defined (__APPLE__)
#define HOT_ATTR
+#define COLD_ATTR
#else
#define HOT_ATTR __attribute__ ((hot))
+#define COLD_ATTR __attribute__ ((cold))
#endif
#define PURE __attribute__ ((__pure__))
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 7e8365eaba..c0cfee2463 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -41,6 +41,54 @@ static inline int futex(volatile int *uaddr, int op, int val, const struct times
}
#endif // ART_USE_FUTEXES
+#if defined(__APPLE__)
+
+// This works on Mac OS 10.6 but hasn't been tested on older releases.
+struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
+ long padding0; // NOLINT(runtime/int) exact match to darwin type
+ int padding1;
+ uint32_t padding2;
+ int16_t padding3;
+ int16_t padding4;
+ uint32_t padding5;
+ pthread_t darwin_pthread_mutex_owner;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t {
+ long padding0; // NOLINT(runtime/int) exact match to darwin type
+ pthread_mutex_t padding1;
+ int padding2;
+ pthread_cond_t padding3;
+ pthread_cond_t padding4;
+ int padding5;
+ int padding6;
+ pthread_t darwin_pthread_rwlock_owner;
+ // ...other stuff we don't care about.
+};
+
+#endif // __APPLE__
+
+#if defined(__GLIBC__)
+
+struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
+ int32_t padding0[2];
+ int owner;
+ // ...other stuff we don't care about.
+};
+
+struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t {
+#ifdef __LP64__
+ int32_t padding0[6];
+#else
+ int32_t padding0[7];
+#endif
+ int writer;
+ // ...other stuff we don't care about.
+};
+
+#endif // __GLIBC__
+
class ScopedContentionRecorder {
public:
ScopedContentionRecorder(BaseMutex* mutex, uint64_t blocked_tid, uint64_t owner_tid)
@@ -185,6 +233,84 @@ inline void ReaderWriterMutex::SharedUnlock(Thread* self) {
#endif
}
+inline bool Mutex::IsExclusiveHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
+ if (kDebugLocking) {
+ // Sanity debug check that if we think it is locked we have it in our held mutexes.
+ if (result && self != NULL && level_ != kMonitorLock && !gAborting) {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
+ }
+ return result;
+}
+
+inline uint64_t Mutex::GetExclusiveOwnerTid() const {
+#if ART_USE_FUTEXES
+ return exclusive_owner_;
+#elif defined(__BIONIC__)
+ return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
+#elif defined(__GLIBC__)
+ return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner;
+#elif defined(__APPLE__)
+ const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_);
+ pthread_t owner = dpmutex->darwin_pthread_mutex_owner;
+ // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING
+ // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1?
+ if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) {
+ return 0;
+ }
+ uint64_t tid;
+ CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
+ return tid;
+#else
+#error unsupported C library
+#endif
+}
+
+inline bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const {
+ DCHECK(self == NULL || self == Thread::Current());
+ bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
+ if (kDebugLocking) {
+ // Sanity that if the pthread thinks we own the lock the Thread agrees.
+ if (self != NULL && result) {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
+ }
+ return result;
+}
+
+inline uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const {
+#if ART_USE_FUTEXES
+ int32_t state = state_;
+ if (state == 0) {
+ return 0; // No owner.
+ } else if (state > 0) {
+ return -1; // Shared.
+ } else {
+ return exclusive_owner_;
+ }
+#else
+#if defined(__BIONIC__)
+ return rwlock_.writerThreadId;
+#elif defined(__GLIBC__)
+ return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer;
+#elif defined(__APPLE__)
+ const darwin_pthread_rwlock_t*
+ dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_);
+ pthread_t owner = dprwlock->darwin_pthread_rwlock_owner;
+ if (owner == (pthread_t)0) {
+ return 0;
+ }
+ uint64_t tid;
+ CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
+ return tid;
+#else
+#error unsupported C library
+#endif
+#endif
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_MUTEX_INL_H_
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index b99e7c9281..249f031df0 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -31,54 +31,6 @@
namespace art {
-#if defined(__APPLE__)
-
-// This works on Mac OS 10.6 but hasn't been tested on older releases.
-struct __attribute__((__may_alias__)) darwin_pthread_mutex_t {
- long padding0; // NOLINT(runtime/int) exact match to darwin type
- int padding1;
- uint32_t padding2;
- int16_t padding3;
- int16_t padding4;
- uint32_t padding5;
- pthread_t darwin_pthread_mutex_owner;
- // ...other stuff we don't care about.
-};
-
-struct __attribute__((__may_alias__)) darwin_pthread_rwlock_t {
- long padding0; // NOLINT(runtime/int) exact match to darwin type
- pthread_mutex_t padding1;
- int padding2;
- pthread_cond_t padding3;
- pthread_cond_t padding4;
- int padding5;
- int padding6;
- pthread_t darwin_pthread_rwlock_owner;
- // ...other stuff we don't care about.
-};
-
-#endif // __APPLE__
-
-#if defined(__GLIBC__)
-
-struct __attribute__((__may_alias__)) glibc_pthread_mutex_t {
- int32_t padding0[2];
- int owner;
- // ...other stuff we don't care about.
-};
-
-struct __attribute__((__may_alias__)) glibc_pthread_rwlock_t {
-#ifdef __LP64__
- int32_t padding0[6];
-#else
- int32_t padding0[7];
-#endif
- int writer;
- // ...other stuff we don't care about.
-};
-
-#endif // __GLIBC__
-
#if ART_USE_FUTEXES
static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds.
@@ -102,17 +54,17 @@ struct AllMutexData {
std::set<BaseMutex*>* all_mutexes;
AllMutexData() : all_mutexes(NULL) {}
};
-static struct AllMutexData all_mutex_data[kAllMutexDataSize];
+static struct AllMutexData gAllMutexData[kAllMutexDataSize];
class ScopedAllMutexesLock {
public:
explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) {
- while (!all_mutex_data->all_mutexes_guard.compare_and_swap(0, reinterpret_cast<int32_t>(mutex))) {
+ while (!gAllMutexData->all_mutexes_guard.compare_and_swap(0, reinterpret_cast<int32_t>(mutex))) {
NanoSleep(100);
}
}
~ScopedAllMutexesLock() {
- while (!all_mutex_data->all_mutexes_guard.compare_and_swap(reinterpret_cast<int32_t>(mutex_), 0)) {
+ while (!gAllMutexData->all_mutexes_guard.compare_and_swap(reinterpret_cast<int32_t>(mutex_), 0)) {
NanoSleep(100);
}
}
@@ -123,7 +75,7 @@ class ScopedAllMutexesLock {
BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) {
if (kLogLockContentions) {
ScopedAllMutexesLock mu(this);
- std::set<BaseMutex*>** all_mutexes_ptr = &all_mutex_data->all_mutexes;
+ std::set<BaseMutex*>** all_mutexes_ptr = &gAllMutexData->all_mutexes;
if (*all_mutexes_ptr == NULL) {
// We leak the global set of all mutexes to avoid ordering issues in global variable
// construction/destruction.
@@ -136,7 +88,7 @@ BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(n
BaseMutex::~BaseMutex() {
if (kLogLockContentions) {
ScopedAllMutexesLock mu(this);
- all_mutex_data->all_mutexes->erase(this);
+ gAllMutexData->all_mutexes->erase(this);
}
}
@@ -144,13 +96,13 @@ void BaseMutex::DumpAll(std::ostream& os) {
if (kLogLockContentions) {
os << "Mutex logging:\n";
ScopedAllMutexesLock mu(reinterpret_cast<const BaseMutex*>(-1));
- std::set<BaseMutex*>* all_mutexes = all_mutex_data->all_mutexes;
+ std::set<BaseMutex*>* all_mutexes = gAllMutexData->all_mutexes;
if (all_mutexes == NULL) {
// No mutexes have been created yet during at startup.
return;
}
typedef std::set<BaseMutex*>::const_iterator It;
- os << "(Contented)\n";
+ os << "(Contended)\n";
for (It it = all_mutexes->begin(); it != all_mutexes->end(); ++it) {
BaseMutex* mutex = *it;
if (mutex->HasEverContended()) {
@@ -175,7 +127,8 @@ void BaseMutex::CheckSafeToWait(Thread* self) {
return;
}
if (kDebugLocking) {
- CHECK(self->GetHeldMutex(level_) == this) << "Waiting on unacquired mutex: " << name_;
+ CHECK(self->GetHeldMutex(level_) == this || level_ == kMonitorLock)
+ << "Waiting on unacquired mutex: " << name_;
bool bad_mutexes_held = false;
for (int i = kLockLevelCount - 1; i >= 0; --i) {
if (i != level_) {
@@ -346,7 +299,7 @@ void Mutex::ExclusiveLock(Thread* self) {
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == 0) {
+ if (LIKELY(cur_state == 0)) {
// Change state from 0 to 1.
done = android_atomic_acquire_cas(0, 1, &state_) == 0;
} else {
@@ -432,14 +385,14 @@ void Mutex::ExclusiveUnlock(Thread* self) {
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == 1) {
+ if (LIKELY(cur_state == 1)) {
// We're no longer the owner.
exclusive_owner_ = 0;
// Change state to 0.
done = android_atomic_release_cas(cur_state, 0, &state_) == 0;
- if (done) { // Spurious fail?
+ if (LIKELY(done)) { // Spurious fail?
// Wake a contender
- if (num_contenders_ > 0) {
+ if (UNLIKELY(num_contenders_ > 0)) {
futex(&state_, FUTEX_WAKE, 1, NULL, NULL, 0);
}
}
@@ -461,41 +414,6 @@ void Mutex::ExclusiveUnlock(Thread* self) {
}
}
-bool Mutex::IsExclusiveHeld(const Thread* self) const {
- DCHECK(self == NULL || self == Thread::Current());
- bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
- if (kDebugLocking) {
- // Sanity debug check that if we think it is locked we have it in our held mutexes.
- if (result && self != NULL && level_ != kMonitorLock && !gAborting) {
- CHECK_EQ(self->GetHeldMutex(level_), this);
- }
- }
- return result;
-}
-
-uint64_t Mutex::GetExclusiveOwnerTid() const {
-#if ART_USE_FUTEXES
- return exclusive_owner_;
-#elif defined(__BIONIC__)
- return static_cast<uint64_t>((mutex_.value >> 16) & 0xffff);
-#elif defined(__GLIBC__)
- return reinterpret_cast<const glibc_pthread_mutex_t*>(&mutex_)->owner;
-#elif defined(__APPLE__)
- const darwin_pthread_mutex_t* dpmutex = reinterpret_cast<const darwin_pthread_mutex_t*>(&mutex_);
- pthread_t owner = dpmutex->darwin_pthread_mutex_owner;
- // 0 for unowned, -1 for PTHREAD_MTX_TID_SWITCHING
- // TODO: should we make darwin_pthread_mutex_owner volatile and recheck until not -1?
- if ((owner == (pthread_t)0) || (owner == (pthread_t)-1)) {
- return 0;
- }
- uint64_t tid;
- CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
- return tid;
-#else
-#error unsupported C library
-#endif
-}
-
void Mutex::Dump(std::ostream& os) const {
os << (recursive_ ? "recursive " : "non-recursive ")
<< name_
@@ -549,7 +467,7 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) {
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == 0) {
+ if (LIKELY(cur_state == 0)) {
// Change state from 0 to -1.
done = android_atomic_acquire_cas(0, -1, &state_) == 0;
} else {
@@ -583,14 +501,14 @@ void ReaderWriterMutex::ExclusiveUnlock(Thread* self) {
bool done = false;
do {
int32_t cur_state = state_;
- if (cur_state == -1) {
+ if (LIKELY(cur_state == -1)) {
// We're no longer the owner.
exclusive_owner_ = 0;
// Change state from -1 to 0.
done = android_atomic_release_cas(-1, 0, &state_) == 0;
- if (done) { // cmpxchg may fail due to noise?
+ if (LIKELY(done)) { // cmpxchg may fail due to noise?
// Wake any waiters.
- if (num_pending_readers_ > 0 || num_pending_writers_ > 0) {
+ if (UNLIKELY(num_pending_readers_ > 0 || num_pending_writers_ > 0)) {
futex(&state_, FUTEX_WAKE, -1, NULL, NULL, 0);
}
}
@@ -687,18 +605,6 @@ bool ReaderWriterMutex::SharedTryLock(Thread* self) {
return true;
}
-bool ReaderWriterMutex::IsExclusiveHeld(const Thread* self) const {
- DCHECK(self == NULL || self == Thread::Current());
- bool result = (GetExclusiveOwnerTid() == SafeGetTid(self));
- if (kDebugLocking) {
- // Sanity that if the pthread thinks we own the lock the Thread agrees.
- if (self != NULL && result) {
- CHECK_EQ(self->GetHeldMutex(level_), this);
- }
- }
- return result;
-}
-
bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const {
DCHECK(self == NULL || self == Thread::Current());
bool result;
@@ -710,37 +616,6 @@ bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const {
return result;
}
-uint64_t ReaderWriterMutex::GetExclusiveOwnerTid() const {
-#if ART_USE_FUTEXES
- int32_t state = state_;
- if (state == 0) {
- return 0; // No owner.
- } else if (state > 0) {
- return -1; // Shared.
- } else {
- return exclusive_owner_;
- }
-#else
-#if defined(__BIONIC__)
- return rwlock_.writerThreadId;
-#elif defined(__GLIBC__)
- return reinterpret_cast<const glibc_pthread_rwlock_t*>(&rwlock_)->writer;
-#elif defined(__APPLE__)
- const darwin_pthread_rwlock_t*
- dprwlock = reinterpret_cast<const darwin_pthread_rwlock_t*>(&rwlock_);
- pthread_t owner = dprwlock->darwin_pthread_rwlock_owner;
- if (owner == (pthread_t)0) {
- return 0;
- }
- uint64_t tid;
- CHECK_PTHREAD_CALL(pthread_threadid_np, (owner, &tid), __FUNCTION__); // Requires Mac OS 10.6
- return tid;
-#else
-#error unsupported C library
-#endif
-#endif
-}
-
void ReaderWriterMutex::Dump(std::ostream& os) const {
os << name_
<< " level=" << static_cast<int>(level_)
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index ee37388f3b..feb8a6c6c1 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -58,7 +58,7 @@ const bool kLogLockContentions = false;
// futex.
const bool kLogLockContentions = false;
#endif
-const size_t kContentionLogSize = 64;
+const size_t kContentionLogSize = 4;
const size_t kContentionLogDataSize = kLogLockContentions ? 1 : 0;
const size_t kAllMutexDataSize = kLogLockContentions ? 1 : 0;
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 36f8ba7fc6..f48c76db23 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -102,10 +102,6 @@ bool FdFile::IsOpened() const {
return fd_ >= 0;
}
-std::string FdFile::GetPath() const {
- return file_path_;
-}
-
bool FdFile::ReadFully(void* buffer, int64_t byte_count) {
char* ptr = static_cast<char*>(buffer);
while (byte_count > 0) {
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 79a0db9eda..19e3511eb4 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -57,7 +57,9 @@ class FdFile : public RandomAccessFile {
// Bonus API.
int Fd() const;
bool IsOpened() const;
- std::string GetPath() const;
+ const std::string& GetPath() const {
+ return file_path_;
+ }
void DisableAutoClose();
bool ReadFully(void* buffer, int64_t byte_count);
bool WriteFully(const void* buffer, int64_t byte_count);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index db4cc00a8b..184e5d4be9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -196,7 +196,9 @@ ClassLinker::ClassLinker(InternTable* intern_table)
class_table_dirty_(false),
intern_table_(intern_table),
portable_resolution_trampoline_(NULL),
- quick_resolution_trampoline_(NULL) {
+ quick_resolution_trampoline_(NULL),
+ portable_imt_conflict_trampoline_(NULL),
+ quick_imt_conflict_trampoline_(NULL) {
CHECK_EQ(arraysize(class_roots_descriptors_), size_t(kClassRootsMax));
}
@@ -283,7 +285,7 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class
SirtRef<mirror::Class>
java_lang_DexCache(self, AllocClass(self, java_lang_Class.get(), sizeof(mirror::DexCacheClass)));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.get());
- java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCacheClass));
+ java_lang_DexCache->SetObjectSize(sizeof(mirror::DexCache));
java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self);
// Constructor, Field, Method, and AbstractMethod are necessary so that FindClass can link members.
@@ -336,6 +338,12 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class
InitializePrimitiveClass(char_class.get(), Primitive::kPrimChar);
SetClassRoot(kPrimitiveChar, char_class.get()); // needs descriptor
+ // Create runtime resolution and imt conflict methods. Also setup the default imt.
+ Runtime* runtime = Runtime::Current();
+ runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
+ runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
+ runtime->SetDefaultImt(runtime->CreateDefaultImt(this));
+
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Object_class = FindSystemClass("Ljava/lang/Object;");
@@ -540,9 +548,10 @@ void ClassLinker::RunRootClinits() {
}
}
-bool ClassLinker::GenerateOatFile(const std::string& dex_filename,
+bool ClassLinker::GenerateOatFile(const char* dex_filename,
int oat_fd,
- const std::string& oat_cache_filename) {
+ const char* oat_cache_filename) {
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
std::string dex2oat_string(GetAndroidRoot());
dex2oat_string += (kIsDebugBuild ? "/bin/dex2oatd" : "/bin/dex2oat");
const char* dex2oat = dex2oat_string.c_str();
@@ -567,7 +576,8 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename,
const char* oat_location_option = oat_location_option_string.c_str();
std::string oat_compiler_filter_string("-compiler-filter:");
- switch (Runtime::Current()->GetCompilerFilter()) {
+ Runtime::CompilerFilter filter = Runtime::Current()->GetCompilerFilter();
+ switch (filter) {
case Runtime::kInterpretOnly:
oat_compiler_filter_string += "interpret-only";
break;
@@ -584,7 +594,7 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename,
oat_compiler_filter_string += "everything";
break;
default:
- LOG(FATAL) << "Unexpected case.";
+ LOG(FATAL) << "Unexpected case: " << filter;
}
const char* oat_compiler_filter_option = oat_compiler_filter_string.c_str();
@@ -633,49 +643,55 @@ bool ClassLinker::GenerateOatFile(const std::string& dex_filename,
int status;
pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (got_pid != pid) {
- PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+ ScopedObjectAccess soa(Thread::Current());
+ ThrowIOException("Failed to create oat file. Waitpid failed: wanted %d, got %d", pid,
+ got_pid);
return false;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << dex2oat << " failed with dex-file=" << dex_filename;
+ ScopedObjectAccess soa(Thread::Current());
+ ThrowIOException("Failed to create oat file. %s failed with dex-file '%s'", dex2oat,
+ dex_filename);
return false;
}
}
return true;
}
-void ClassLinker::RegisterOatFile(const OatFile& oat_file) {
+const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
WriterMutexLock mu(Thread::Current(), dex_lock_);
- RegisterOatFileLocked(oat_file);
-}
-
-void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) {
- dex_lock_.AssertExclusiveHeld(Thread::Current());
- if (kIsDebugBuild) {
- for (size_t i = 0; i < oat_files_.size(); ++i) {
- CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation();
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ if (UNLIKELY(oat_file->GetLocation() == oat_files_[i]->GetLocation())) {
+ VLOG(class_linker) << "Attempt to register oat file that's already registered: "
+ << oat_file->GetLocation();
+ for (size_t j = i; j < oat_files_.size(); ++j) {
+ CHECK_NE(oat_file, oat_files_[j]) << "Attempt to re-register dex file.";
+ }
+ delete oat_file;
+ return oat_files_[i];
}
}
- VLOG(class_linker) << "Registering " << oat_file.GetLocation();
- oat_files_.push_back(&oat_file);
+ VLOG(class_linker) << "Registering " << oat_file->GetLocation();
+ oat_files_.push_back(oat_file);
+ return oat_file;
}
OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) {
VLOG(startup) << "ClassLinker::GetImageOatFile entering";
- OatFile& oat_file = space->ReleaseOatFile();
- WriterMutexLock mu(Thread::Current(), dex_lock_);
- RegisterOatFileLocked(oat_file);
+ OatFile* oat_file = space->ReleaseOatFile();
+ CHECK_EQ(RegisterOatFile(oat_file), oat_file);
VLOG(startup) << "ClassLinker::GetImageOatFile exiting";
- return oat_file;
+ return *oat_file;
}
const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- return FindOpenedOatFileFromDexLocation(dex_file.GetLocation(), dex_file.GetLocationChecksum());
+ return FindOpenedOatFileFromDexLocation(dex_file.GetLocation().c_str(),
+ dex_file.GetLocationChecksum());
}
-const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const std::string& dex_location,
+const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const char* dex_location,
uint32_t dex_location_checksum) {
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (size_t i = 0; i < oat_files_.size(); i++) {
const OatFile* oat_file = oat_files_[i];
DCHECK(oat_file != NULL);
@@ -689,82 +705,83 @@ const OatFile* ClassLinker::FindOpenedOatFileFromDexLocation(const std::string&
return NULL;
}
-const DexFile* ClassLinker::FindDexFileInOatLocation(const std::string& dex_location,
+const DexFile* ClassLinker::FindDexFileInOatLocation(const char* dex_location,
uint32_t dex_location_checksum,
- const std::string& oat_location) {
+ const char* oat_location,
+ std::string* error_msg) {
UniquePtr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, NULL,
- !Runtime::Current()->IsCompiler()));
- if (oat_file.get() == NULL) {
- VLOG(class_linker) << "Failed to find existing oat file at " << oat_location;
- return NULL;
+ !Runtime::Current()->IsCompiler(),
+ error_msg));
+ if (oat_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
+ error_msg->c_str());
+ return nullptr;
}
Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader();
uint32_t expected_image_oat_checksum = image_header.GetOatChecksum();
uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum();
if (expected_image_oat_checksum != actual_image_oat_checksum) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location
- << " with expected image oat checksum of " << expected_image_oat_checksum
- << ", found " << actual_image_oat_checksum;
- return NULL;
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of "
+ "0x%x, found 0x%x", oat_location, expected_image_oat_checksum,
+ actual_image_oat_checksum);
+ return nullptr;
}
uint32_t expected_image_oat_offset = reinterpret_cast<uint32_t>(image_header.GetOatDataBegin());
uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin();
if (expected_image_oat_offset != actual_image_oat_offset) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location
- << " with expected image oat offset " << expected_image_oat_offset
- << ", found " << actual_image_oat_offset;
- return NULL;
- }
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
- if (oat_dex_file == NULL) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location << " containing " << dex_location;
- return NULL;
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %ud, "
+ "found %ud", oat_location, expected_image_oat_offset,
+ actual_image_oat_offset);
+ return nullptr;
+ }
+ // TODO: this registers the oat file now as we may use the oat_dex_file later and we want the
+ // intern behavior of RegisterOatFile. However, if we take an early return we could remove
+ // the oat file.
+ const OatFile* opened_oat_file = RegisterOatFile(oat_file.release());
+ const OatFile::OatDexFile* oat_dex_file = opened_oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location,
+ dex_location);
+ return nullptr;
}
uint32_t expected_dex_checksum = dex_location_checksum;
uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum();
if (expected_dex_checksum != actual_dex_checksum) {
- VLOG(class_linker) << "Failed to find oat file at " << oat_location
- << " with expected dex checksum of " << expected_dex_checksum
- << ", found " << actual_dex_checksum;
- return NULL;
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, "
+ "found 0x%x", oat_location, expected_dex_checksum,
+ actual_dex_checksum);
+ return nullptr;
}
- RegisterOatFileLocked(*oat_file.release());
- return oat_dex_file->OpenDexFile();
-}
-
-const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const std::string& dex_location,
- uint32_t dex_location_checksum,
- const std::string& oat_location) {
- WriterMutexLock mu(Thread::Current(), dex_lock_);
- return FindOrCreateOatFileForDexLocationLocked(dex_location, dex_location_checksum, oat_location);
+ return oat_dex_file->OpenDexFile(error_msg);
}
class ScopedFlock {
public:
ScopedFlock() {}
- bool Init(const std::string& filename) {
+ bool Init(const char* filename, std::string* error_msg) {
while (true) {
- file_.reset(OS::OpenFileWithFlags(filename.c_str(), O_CREAT | O_RDWR));
+ file_.reset(OS::OpenFileWithFlags(filename, O_CREAT | O_RDWR));
if (file_.get() == NULL) {
- LOG(ERROR) << "Failed to open file: " << filename;
+ *error_msg = StringPrintf("Failed to open file '%s'", filename);
return false;
}
int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX));
if (flock_result != 0) {
- PLOG(ERROR) << "Failed to lock file: " << filename;
+ *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno));
return false;
}
struct stat fstat_stat;
int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat));
if (fstat_result != 0) {
- PLOG(ERROR) << "Failed to fstat: " << filename;
+ *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno));
return false;
}
struct stat stat_stat;
- int stat_result = TEMP_FAILURE_RETRY(stat(filename.c_str(), &stat_stat));
+ int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat));
if (stat_result != 0) {
PLOG(WARNING) << "Failed to stat, will retry: " << filename;
// ENOENT can happen if someone racing with us unlinks the file we created so just retry.
@@ -795,49 +812,54 @@ class ScopedFlock {
DISALLOW_COPY_AND_ASSIGN(ScopedFlock);
};
-const DexFile* ClassLinker::FindOrCreateOatFileForDexLocationLocked(const std::string& dex_location,
- uint32_t dex_location_checksum,
- const std::string& oat_location) {
+const DexFile* ClassLinker::FindOrCreateOatFileForDexLocation(const char* dex_location,
+ uint32_t dex_location_checksum,
+ const char* oat_location,
+ std::string* error_msg) {
// We play a locking game here so that if two different processes
// race to generate (or worse, one tries to open a partial generated
// file) we will be okay. This is actually common with apps that use
// DexClassLoader to work around the dex method reference limit and
// that have a background service running in a separate process.
ScopedFlock scoped_flock;
- if (!scoped_flock.Init(oat_location)) {
- LOG(ERROR) << "Failed to open locked oat file: " << oat_location;
- return NULL;
+ if (!scoped_flock.Init(oat_location, error_msg)) {
+ return nullptr;
}
// Check if we already have an up-to-date output file
- const DexFile* dex_file = FindDexFileInOatLocation(dex_location,
- dex_location_checksum,
- oat_location);
- if (dex_file != NULL) {
+ const DexFile* dex_file = FindDexFileInOatLocation(dex_location, dex_location_checksum,
+ oat_location, error_msg);
+ if (dex_file != nullptr) {
return dex_file;
}
+ VLOG(class_linker) << "Failed to find dex file '" << dex_location << "' in oat location '"
+ << oat_location << "': " << *error_msg;
+ error_msg->clear();
// Generate the output oat file for the dex file
VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
if (!GenerateOatFile(dex_location, scoped_flock.GetFile().Fd(), oat_location)) {
- LOG(ERROR) << "Failed to generate oat file: " << oat_location;
- return NULL;
+ CHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
}
const OatFile* oat_file = OatFile::Open(oat_location, oat_location, NULL,
- !Runtime::Current()->IsCompiler());
- if (oat_file == NULL) {
- LOG(ERROR) << "Failed to open generated oat file: " << oat_location;
- return NULL;
- }
- RegisterOatFileLocked(*oat_file);
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
- if (oat_dex_file == NULL) {
- LOG(ERROR) << "Failed to find dex file " << dex_location
- << " (checksum " << dex_location_checksum
- << ") in generated oat file: " << oat_location;
- return NULL;
+ !Runtime::Current()->IsCompiler(),
+ error_msg);
+ if (oat_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open generated oat file '%s': %s",
+ oat_location, error_msg->c_str());
+ return nullptr;
+ }
+ oat_file = RegisterOatFile(oat_file);
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to find dex file '%s' (checksum 0x%x) in generated out file "
+ "'%s'", dex_location, dex_location_checksum, oat_location);
+ return nullptr;
}
- const DexFile* result = oat_dex_file->OpenDexFile();
+ const DexFile* result = oat_dex_file->OpenDexFile(error_msg);
+ CHECK(result != nullptr) << *error_msg;
CHECK_EQ(dex_location_checksum, result->GetLocationChecksum())
<< "dex_location=" << dex_location << " oat_location=" << oat_location << std::hex
<< " dex_location_checksum=" << dex_location_checksum
@@ -846,8 +868,9 @@ const DexFile* ClassLinker::FindOrCreateOatFileForDexLocationLocked(const std::s
}
bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum) {
+ const char* dex_location,
+ uint32_t dex_location_checksum,
+ std::string* error_msg) {
Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader();
uint32_t image_oat_checksum = image_header.GetOatChecksum();
@@ -857,14 +880,14 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
if (oat_dex_file == NULL) {
- LOG(ERROR) << "oat file " << oat_file->GetLocation()
- << " does not contain contents for " << dex_location
- << " with checksum " << dex_location_checksum;
+ *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
+ oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file->GetOatDexFiles();
for (size_t i = 0; i < oat_dex_files.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
- LOG(ERROR) << "oat file " << oat_file->GetLocation()
- << " contains contents for " << oat_dex_file->GetDexFileLocation();
+ *error_msg += StringPrintf("\noat file '%s' contains contents for '%s'",
+ oat_file->GetLocation().c_str(),
+ oat_dex_file->GetDexFileLocation().c_str());
}
return false;
}
@@ -875,116 +898,123 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
}
if (!image_check) {
- std::string image_file(image_header.GetImageRoot(
- ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8());
- LOG(WARNING) << "oat file " << oat_file->GetLocation()
- << " mismatch (" << std::hex << oat_file->GetOatHeader().GetImageFileLocationOatChecksum()
- << ", " << oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
- << ") with " << image_file
- << " (" << image_oat_checksum << ", " << std::hex << image_oat_data_begin << ")";
+ ScopedObjectAccess soa(Thread::Current());
+ mirror::String* oat_location = image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString();
+ std::string image_file(oat_location->ToModifiedUtf8());
+ *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d) with '%s' (0x%x, %d)",
+ oat_file->GetLocation().c_str(),
+ oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
+ oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
+ image_file.c_str(), image_oat_checksum, image_oat_data_begin);
}
if (!dex_check) {
- LOG(WARNING) << "oat file " << oat_file->GetLocation()
- << " mismatch (" << std::hex << oat_dex_file->GetDexFileLocationChecksum()
- << ") with " << dex_location
- << " (" << std::hex << dex_location_checksum << ")";
+ *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
+ oat_file->GetLocation().c_str(),
+ oat_dex_file->GetDexFileLocationChecksum(),
+ dex_location, dex_location_checksum);
}
return false;
}
-const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum) {
- bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum);
+const DexFile* ClassLinker::VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location,
+ const char* dex_location,
+ std::string* error_msg,
+ bool* open_failed) {
+ UniquePtr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg));
+ if (oat_file.get() == nullptr) {
+ *open_failed = true;
+ return nullptr;
+ }
+ *open_failed = false;
+ uint32_t dex_location_checksum;
+ if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
+ // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
+ // up-to-date. This is the common case in user builds for jar's and apk's in the /system
+ // directory.
+ const OatFile* opened_oat_file = oat_file.release();
+ opened_oat_file = RegisterOatFile(opened_oat_file);
+ const OatFile::OatDexFile* oat_dex_file = opened_oat_file->GetOatDexFile(dex_location, NULL);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
+ "dex file '%s': %s", oat_file_location.c_str(), dex_location,
+ error_msg->c_str());
+ return nullptr;
+ }
+ return oat_dex_file->OpenDexFile(error_msg);
+ }
+
+ bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum,
+ error_msg);
if (!verified) {
- delete oat_file;
- return NULL;
+ return nullptr;
}
- RegisterOatFileLocked(*oat_file);
- return oat_file->GetOatDexFile(dex_location, &dex_location_checksum)->OpenDexFile();
+ const OatFile* opened_oat_file = oat_file.release();
+ opened_oat_file = RegisterOatFile(opened_oat_file);
+ return opened_oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum)->OpenDexFile(error_msg);
}
-const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const std::string& dex_location,
- uint32_t dex_location_checksum) {
- WriterMutexLock mu(Thread::Current(), dex_lock_);
-
+const DexFile* ClassLinker::FindDexFileInOatFileFromDexLocation(const char* dex_location,
+ uint32_t dex_location_checksum,
+ std::string* error_msg) {
const OatFile* open_oat_file = FindOpenedOatFileFromDexLocation(dex_location,
dex_location_checksum);
- if (open_oat_file != NULL) {
- return open_oat_file->GetOatDexFile(dex_location, &dex_location_checksum)->OpenDexFile();
+ if (open_oat_file != nullptr) {
+ const OatFile::OatDexFile* oat_dex_file = open_oat_file->GetOatDexFile(dex_location,
+ &dex_location_checksum);
+ return oat_dex_file->OpenDexFile(error_msg);
}
// Look for an existing file next to dex. for example, for
// /foo/bar/baz.jar, look for /foo/bar/baz.odex.
std::string odex_filename(OatFile::DexFilenameToOdexFilename(dex_location));
- UniquePtr<const OatFile> oat_file(FindOatFileFromOatLocationLocked(odex_filename));
- if (oat_file.get() != NULL) {
- uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
- // If no classes.dex found in dex_location, it has been stripped, assume oat is up-to-date.
- // This is the common case in user builds for jar's and apk's in the /system directory.
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, NULL);
- CHECK(oat_dex_file != NULL) << odex_filename << " " << dex_location;
- RegisterOatFileLocked(*oat_file);
- return oat_dex_file->OpenDexFile();
- }
- const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(oat_file.release(),
- dex_location,
- dex_location_checksum);
- if (dex_file != NULL) {
- return dex_file;
- }
- }
- // Look for an existing file in the dalvik-cache, validating the result if found
- // not found in /foo/bar/baz.odex? try /data/dalvik-cache/foo@bar@baz.jar@classes.dex
+ bool open_failed;
+ const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(odex_filename, dex_location,
+ error_msg, &open_failed);
+ if (dex_file != nullptr) {
+ return dex_file;
+ }
+ std::string cache_error_msg;
std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location));
- oat_file.reset(FindOatFileFromOatLocationLocked(cache_location));
- if (oat_file.get() != NULL) {
- uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
- LOG(WARNING) << "Failed to compute checksum: " << dex_location;
- return NULL;
- }
- const DexFile* dex_file = VerifyAndOpenDexFileFromOatFile(oat_file.release(),
- dex_location,
- dex_location_checksum);
- if (dex_file != NULL) {
- return dex_file;
- }
- if (TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
- PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location;
- }
+ dex_file = VerifyAndOpenDexFileFromOatFile(cache_location, dex_location, &cache_error_msg,
+ &open_failed);
+ if (dex_file != nullptr) {
+ return dex_file;
}
- LOG(INFO) << "Failed to open oat file from " << odex_filename << " or " << cache_location << ".";
+ if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
+ PLOG(FATAL) << "Failed to remove obsolete oat file from " << cache_location;
+ }
+ VLOG(class_linker) << "Failed to open oat file from " << odex_filename
+ << " (error '" << *error_msg << "') or " << cache_location
+ << " (error '" << cache_error_msg << "').";
// Try to generate oat file if it wasn't found or was obsolete.
- std::string oat_cache_filename(GetDalvikCacheFilenameOrDie(dex_location));
- return FindOrCreateOatFileForDexLocationLocked(dex_location, dex_location_checksum, oat_cache_filename);
+ error_msg->clear();
+ return FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum,
+ cache_location.c_str(), error_msg);
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (size_t i = 0; i < oat_files_.size(); i++) {
const OatFile* oat_file = oat_files_[i];
- DCHECK(oat_file != NULL);
+ DCHECK(oat_file != nullptr);
if (oat_file->GetLocation() == oat_location) {
return oat_file;
}
}
- return NULL;
-}
-
-const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- return FindOatFileFromOatLocationLocked(oat_location);
+ return nullptr;
}
-const OatFile* ClassLinker::FindOatFileFromOatLocationLocked(const std::string& oat_location) {
+const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
+ std::string* error_msg) {
const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
- if (oat_file != NULL) {
+ if (oat_file != nullptr) {
return oat_file;
}
- oat_file = OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler());
+ oat_file = OatFile::Open(oat_location, oat_location, NULL, !Runtime::Current()->IsCompiler(),
+ error_msg);
if (oat_file == NULL) {
return NULL;
}
@@ -1023,6 +1053,8 @@ void ClassLinker::InitFromImage() {
CHECK(oat_file.GetOatHeader().GetImageFileLocation().empty());
portable_resolution_trampoline_ = oat_file.GetOatHeader().GetPortableResolutionTrampoline();
quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
+ portable_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetPortableImtConflictTrampoline();
+ quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
@@ -1041,12 +1073,15 @@ void ClassLinker::InitFromImage() {
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
SirtRef<mirror::DexCache> dex_cache(self, dex_caches->Get(i));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
- const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location, NULL);
+ const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
+ nullptr);
CHECK(oat_dex_file != NULL) << oat_file.GetLocation() << " " << dex_file_location;
- const DexFile* dex_file = oat_dex_file->OpenDexFile();
+ std::string error_msg;
+ const DexFile* dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file == NULL) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
- << " from within oat file " << oat_file.GetLocation();
+ << " from within oat file " << oat_file.GetLocation()
+ << " error '" << error_msg << "'";
}
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
@@ -1094,13 +1129,14 @@ void ClassLinker::InitFromImage() {
// reinit references to when reinitializing a ClassLinker from a
// mapped image.
void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool clean_dirty) {
- visitor(class_roots_, arg);
+ class_roots_ = down_cast<mirror::ObjectArray<mirror::Class>*>(visitor(class_roots_, arg));
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
if (!only_dirty || dex_caches_dirty_) {
- for (mirror::DexCache* dex_cache : dex_caches_) {
- visitor(dex_cache, arg);
+ for (mirror::DexCache*& dex_cache : dex_caches_) {
+ dex_cache = down_cast<mirror::DexCache*>(visitor(dex_cache, arg));
+ DCHECK(dex_cache != nullptr);
}
if (clean_dirty) {
dex_caches_dirty_ = false;
@@ -1111,8 +1147,9 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, b
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
if (!only_dirty || class_table_dirty_) {
- for (const std::pair<size_t, mirror::Class*>& it : class_table_) {
- visitor(it.second, arg);
+ for (std::pair<const size_t, mirror::Class*>& it : class_table_) {
+ it.second = down_cast<mirror::Class*>(visitor(it.second, arg));
+ DCHECK(it.second != nullptr);
}
if (clean_dirty) {
class_table_dirty_ = false;
@@ -1123,7 +1160,8 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, b
// handle image roots by using the MS/CMS rescanning of dirty cards.
}
- visitor(array_iftable_, arg);
+ array_iftable_ = reinterpret_cast<mirror::IfTable*>(visitor(array_iftable_, arg));
+ DCHECK(array_iftable_ != nullptr);
}
void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
@@ -1507,7 +1545,7 @@ const OatFile::OatClass* ClassLinker::GetOatClass(const DexFile& dex_file, uint1
const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
CHECK(oat_file != NULL) << dex_file.GetLocation();
uint dex_location_checksum = dex_file.GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation(),
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
&dex_location_checksum);
CHECK(oat_dex_file != NULL) << dex_file.GetLocation();
const OatFile::OatClass* oat_class = oat_dex_file->GetOatClass(class_def_idx);
@@ -1630,21 +1668,22 @@ static bool NeedsInterpreter(const mirror::ArtMethod* method, const void* code)
}
void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) {
- ClassHelper kh(klass);
- const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
- CHECK(dex_class_def != NULL);
- const DexFile& dex_file = kh.GetDexFile();
- const byte* class_data = dex_file.GetClassData(*dex_class_def);
- if (class_data == NULL) {
- return; // no fields or methods - for example a marker interface
+ if (klass->NumDirectMethods() == 0) {
+ return; // No direct methods => no static methods.
}
Runtime* runtime = Runtime::Current();
if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) {
- // OAT file unavailable
- return;
+ return; // OAT file unavailable.
}
+ ClassHelper kh(klass);
+ const DexFile& dex_file = kh.GetDexFile();
+ const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
+ CHECK(dex_class_def != nullptr);
+ const byte* class_data = dex_file.GetClassData(*dex_class_def);
+ // There should always be class data if there were direct methods.
+ CHECK(class_data != nullptr) << PrettyDescriptor(klass);
UniquePtr<const OatFile::OatClass> oat_class(GetOatClass(dex_file, klass->GetDexClassDefIndex()));
- CHECK(oat_class.get() != NULL);
+ CHECK(oat_class.get() != nullptr);
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
while (it.HasNextStaticField()) {
@@ -1653,7 +1692,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) {
while (it.HasNextInstanceField()) {
it.Next();
}
- // Link the code of methods skipped by LinkCode
+ // Link the code of methods skipped by LinkCode.
for (size_t method_index = 0; it.HasNextDirectMethod(); ++method_index, it.Next()) {
mirror::ArtMethod* method = klass->GetDirectMethod(method_index);
if (!method->IsStatic()) {
@@ -1849,7 +1888,7 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file
SirtRef<mirror::Class>& klass) {
uint32_t dex_method_idx = it.GetMemberIndex();
const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
- StringPiece method_name(dex_file.GetMethodName(method_id));
+ const char* method_name = dex_file.StringDataByIdx(method_id.name_idx_);
mirror::ArtMethod* dst = AllocArtMethod(self);
if (UNLIKELY(dst == NULL)) {
@@ -1861,47 +1900,50 @@ mirror::ArtMethod* ClassLinker::LoadMethod(Thread* self, const DexFile& dex_file
const char* old_cause = self->StartAssertNoThreadSuspension("LoadMethod");
dst->SetDexMethodIndex(dex_method_idx);
dst->SetDeclaringClass(klass.get());
-
- if (method_name == "finalize") {
- // Create the prototype for a signature of "()V"
- const DexFile::StringId* void_string_id = dex_file.FindStringId("V");
- if (void_string_id != NULL) {
- const DexFile::TypeId* void_type_id =
- dex_file.FindTypeId(dex_file.GetIndexForStringId(*void_string_id));
- if (void_type_id != NULL) {
- std::vector<uint16_t> no_args;
- const DexFile::ProtoId* finalizer_proto =
- dex_file.FindProtoId(dex_file.GetIndexForTypeId(*void_type_id), no_args);
- if (finalizer_proto != NULL) {
- // We have the prototype in the dex file
- if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged
- klass->SetFinalizable();
- } else {
- ClassHelper kh(klass.get());
- StringPiece klass_descriptor(kh.GetDescriptor());
- // The Enum class declares a "final" finalize() method to prevent subclasses from
- // introducing a finalizer. We don't want to set the finalizable flag for Enum or its
- // subclasses, so we exclude it here.
- // We also want to avoid setting the flag on Object, where we know that finalize() is
- // empty.
- if (klass_descriptor != "Ljava/lang/Object;" &&
- klass_descriptor != "Ljava/lang/Enum;") {
- klass->SetFinalizable();
- }
- }
- }
- }
- }
- }
dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
- dst->SetAccessFlags(it.GetMemberAccessFlags());
dst->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods());
dst->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes());
dst->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage());
- CHECK(dst->IsArtMethod());
+ uint32_t access_flags = it.GetMemberAccessFlags();
+
+ if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
+ // Set finalizable flag on declaring class.
+ if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) {
+ // Void return type.
+ if (klass->GetClassLoader() != NULL) { // All non-boot finalizer methods are flagged
+ klass->SetFinalizable();
+ } else {
+ ClassHelper kh(klass.get());
+ const char* klass_descriptor = kh.GetDescriptor();
+ // The Enum class declares a "final" finalize() method to prevent subclasses from
+ // introducing a finalizer. We don't want to set the finalizable flag for Enum or its
+ // subclasses, so we exclude it here.
+ // We also want to avoid setting the flag on Object, where we know that finalize() is
+ // empty.
+ if ((strcmp("Ljava/lang/Object;", klass_descriptor) != 0) &&
+ (strcmp("Ljava/lang/Enum;", klass_descriptor) != 0)) {
+ klass->SetFinalizable();
+ }
+ }
+ }
+ } else if (method_name[0] == '<') {
+ // Fix broken access flags for initializers. Bug 11157540.
+ bool is_init = (strcmp("<init>", method_name) == 0);
+ bool is_clinit = !is_init && (strcmp("<clinit>", method_name) == 0);
+ if (UNLIKELY(!is_init && !is_clinit)) {
+ LOG(WARNING) << "Unexpected '<' at start of method name " << method_name;
+ } else {
+ if (UNLIKELY((access_flags & kAccConstructor) == 0)) {
+ LOG(WARNING) << method_name << " didn't have expected constructor access flag in class "
+ << PrettyDescriptor(klass.get()) << " in dex file " << dex_file.GetLocation();
+ access_flags |= kAccConstructor;
+ }
+ }
+ }
+ dst->SetAccessFlags(access_flags);
self->EndAssertNoThreadSuspension(old_cause);
return dst;
@@ -2229,7 +2271,8 @@ bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader*
++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
- if (strcmp(kh.GetDescriptor(), descriptor) == 0 && klass->GetClassLoader() == class_loader) {
+ if ((klass->GetClassLoader() == class_loader) &&
+ (strcmp(descriptor, kh.GetDescriptor()) == 0)) {
class_table_.erase(it);
return true;
}
@@ -2275,15 +2318,17 @@ mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor,
for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) {
mirror::Class* klass = it->second;
kh.ChangeClass(klass);
- if (klass->GetClassLoader() == class_loader && strcmp(descriptor, kh.GetDescriptor()) == 0) {
+ if ((klass->GetClassLoader() == class_loader) &&
+ (strcmp(descriptor, kh.GetDescriptor()) == 0)) {
if (kIsDebugBuild) {
// Check for duplicates in the table.
for (++it; it != end && it->first == hash; ++it) {
mirror::Class* klass2 = it->second;
kh.ChangeClass(klass2);
- CHECK(!(strcmp(descriptor, kh.GetDescriptor()) == 0 && klass2->GetClassLoader() == class_loader))
- << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
- << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
+ CHECK(!((klass2->GetClassLoader() == class_loader) &&
+ (strcmp(descriptor, kh.GetDescriptor()) == 0)))
+ << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " "
+ << PrettyClass(klass2) << " " << klass2 << " " << klass2->GetClassLoader();
}
}
return klass;
@@ -2547,14 +2592,14 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class
CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
uint dex_location_checksum = dex_file.GetLocationChecksum();
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation(),
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
&dex_location_checksum);
CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
- const char* descriptor = ClassHelper(klass).GetDescriptor();
uint16_t class_def_index = klass->GetDexClassDefIndex();
UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
CHECK(oat_class.get() != NULL)
- << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor;
+ << dex_file.GetLocation() << " " << PrettyClass(klass) << " "
+ << ClassHelper(klass).GetDescriptor();
oat_file_class_status = oat_class->GetStatus();
if (oat_file_class_status == mirror::Class::kStatusVerified ||
oat_file_class_status == mirror::Class::kStatusInitialized) {
@@ -2593,7 +2638,8 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class
return false;
}
LOG(FATAL) << "Unexpected class status: " << oat_file_class_status
- << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " " << descriptor;
+ << " " << dex_file.GetLocation() << " " << PrettyClass(klass) << " "
+ << ClassHelper(klass).GetDescriptor();
return false;
}
@@ -2835,12 +2881,12 @@ static void CheckProxyConstructor(mirror::ArtMethod* constructor)
CHECK(constructor->IsConstructor());
MethodHelper mh(constructor);
CHECK_STREQ(mh.GetName(), "<init>");
- CHECK_EQ(mh.GetSignature(), std::string("(Ljava/lang/reflect/InvocationHandler;)V"));
+ CHECK_STREQ(mh.GetSignature().ToString().c_str(), "(Ljava/lang/reflect/InvocationHandler;)V");
DCHECK(constructor->IsPublic());
}
mirror::ArtMethod* ClassLinker::CreateProxyMethod(Thread* self, SirtRef<mirror::Class>& klass,
- SirtRef<mirror::ArtMethod>& prototype) {
+ SirtRef<mirror::ArtMethod>& prototype) {
// Ensure prototype is in dex cache so that we can use the dex cache to look up the overridden
// prototype method
prototype->GetDeclaringClass()->GetDexCache()->SetResolvedMethod(prototype->GetDexMethodIndex(),
@@ -2899,12 +2945,12 @@ static void CheckProxyMethod(mirror::ArtMethod* method,
static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
bool can_init_parents)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (can_init_statics && can_init_statics) {
+ if (can_init_statics && can_init_parents) {
return true;
}
if (!can_init_statics) {
// Check if there's a class initializer.
- mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
+ mirror::ArtMethod* clinit = klass->FindClassInitializer();
if (clinit != NULL) {
return false;
}
@@ -2923,7 +2969,7 @@ static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
if (!can_init_parents && !super_class->IsInitialized()) {
return false;
} else {
- if (!CanWeInitializeClass(super_class, can_init_statics, true)) {
+ if (!CanWeInitializeClass(super_class, can_init_statics, can_init_parents)) {
return false;
}
}
@@ -3051,7 +3097,7 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics,
}
}
- mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
+ mirror::ArtMethod* clinit = klass->FindClassInitializer();
if (clinit != NULL) {
CHECK(can_init_statics);
if (LIKELY(Runtime::Current()->IsStarted())) {
@@ -3480,6 +3526,8 @@ bool ClassLinker::LinkVirtualMethods(SirtRef<mirror::Class>& klass) {
bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass,
mirror::ObjectArray<mirror::Class>* interfaces) {
+ // Set the imt table to be all conflicts by default.
+ klass->SetImTable(Runtime::Current()->GetDefaultImt());
size_t super_ifcount;
if (klass->HasSuperClass()) {
super_ifcount = klass->GetSuperClass()->GetIfTableCount();
@@ -3587,6 +3635,13 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass,
if (klass->IsInterface()) {
return true;
}
+ // Allocate imtable
+ bool imtable_changed = false;
+ SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, AllocArtMethodArray(self, kImtSize));
+ if (UNLIKELY(imtable.get() == NULL)) {
+ CHECK(self->IsExceptionPending()); // OOME.
+ return false;
+ }
std::vector<mirror::ArtMethod*> miranda_list;
MethodHelper vtable_mh(NULL, this);
MethodHelper interface_mh(NULL, this);
@@ -3626,6 +3681,14 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass,
return false;
}
method_array->Set(j, vtable_method);
+ // Place method in imt if entry is empty, place conflict otherwise.
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % kImtSize;
+ if (imtable->Get(imt_index) == NULL) {
+ imtable->Set(imt_index, vtable_method);
+ imtable_changed = true;
+ } else {
+ imtable->Set(imt_index, Runtime::Current()->GetImtConflictMethod());
+ }
break;
}
}
@@ -3657,6 +3720,16 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass,
}
}
}
+ if (imtable_changed) {
+ // Fill in empty entries in interface method table with conflict.
+ mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
+ for (size_t i = 0; i < kImtSize; i++) {
+ if (imtable->Get(i) == NULL) {
+ imtable->Set(i, imt_conflict_method);
+ }
+ }
+ klass->SetImTable(imtable.get());
+ }
if (!miranda_list.empty()) {
int old_method_count = klass->NumVirtualMethods();
int new_method_count = old_method_count + miranda_list.size();
@@ -3741,10 +3814,10 @@ struct LinkFieldsComparator {
// same basic group? then sort by string.
fh_->ChangeField(field1);
- StringPiece name1(fh_->GetName());
+ const char* name1 = fh_->GetName();
fh_->ChangeField(field2);
- StringPiece name2(fh_->GetName());
- return name1 < name2;
+ const char* name2 = fh_->GetName();
+ return strcmp(name1, name2) < 0;
}
FieldHelper* fh_;
@@ -3778,7 +3851,9 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) {
// minimizes disruption of C++ version such as Class and Method.
std::deque<mirror::ArtField*> grouped_and_sorted_fields;
for (size_t i = 0; i < num_fields; i++) {
- grouped_and_sorted_fields.push_back(fields->Get(i));
+ mirror::ArtField* f = fields->Get(i);
+ CHECK(f != NULL);
+ grouped_and_sorted_fields.push_back(f);
}
FieldHelper fh(NULL, this);
std::sort(grouped_and_sorted_fields.begin(),
@@ -3845,7 +3920,7 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) {
// We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it.
if (!is_static &&
- StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;") {
+ (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)) {
// We know there are no non-reference fields in the Reference classes, and we know
// that 'referent' is alphabetically last, so this is easy...
CHECK_EQ(num_reference_fields, num_fields);
@@ -3854,39 +3929,39 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) {
--num_reference_fields;
}
-#ifndef NDEBUG
- // Make sure that all reference fields appear before
- // non-reference fields, and all double-wide fields are aligned.
- bool seen_non_ref = false;
- for (size_t i = 0; i < num_fields; i++) {
- mirror::ArtField* field = fields->Get(i);
- if (false) { // enable to debug field layout
- LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance")
- << " class=" << PrettyClass(klass.get())
- << " field=" << PrettyField(field)
- << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()),
- false);
- }
- fh.ChangeField(field);
- Primitive::Type type = fh.GetTypeAsPrimitiveType();
- bool is_primitive = type != Primitive::kPrimNot;
- if (StringPiece(ClassHelper(klass.get(), this).GetDescriptor()) == "Ljava/lang/ref/Reference;" &&
- StringPiece(fh.GetName()) == "referent") {
- is_primitive = true; // We lied above, so we have to expect a lie here.
- }
- if (is_primitive) {
- if (!seen_non_ref) {
- seen_non_ref = true;
- DCHECK_EQ(num_reference_fields, i);
+ if (kIsDebugBuild) {
+ // Make sure that all reference fields appear before
+ // non-reference fields, and all double-wide fields are aligned.
+ bool seen_non_ref = false;
+ for (size_t i = 0; i < num_fields; i++) {
+ mirror::ArtField* field = fields->Get(i);
+ if (false) { // enable to debug field layout
+ LOG(INFO) << "LinkFields: " << (is_static ? "static" : "instance")
+ << " class=" << PrettyClass(klass.get())
+ << " field=" << PrettyField(field)
+ << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()),
+ false);
+ }
+ fh.ChangeField(field);
+ Primitive::Type type = fh.GetTypeAsPrimitiveType();
+ bool is_primitive = type != Primitive::kPrimNot;
+ if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)
+ && (strcmp("referent", fh.GetName()) == 0)) {
+ is_primitive = true; // We lied above, so we have to expect a lie here.
+ }
+ if (is_primitive) {
+ if (!seen_non_ref) {
+ seen_non_ref = true;
+ DCHECK_EQ(num_reference_fields, i);
+ }
+ } else {
+ DCHECK(!seen_non_ref);
}
- } else {
- DCHECK(!seen_non_ref);
+ }
+ if (!seen_non_ref) {
+ DCHECK_EQ(num_fields, num_reference_fields);
}
}
- if (!seen_non_ref) {
- DCHECK_EQ(num_fields, num_reference_fields);
- }
-#endif
size = field_offset.Uint32Value();
// Update klass
if (is_static) {
@@ -3896,6 +3971,11 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) {
klass->SetNumReferenceInstanceFields(num_reference_fields);
if (!klass->IsVariableSize()) {
DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get(), this).GetDescriptor();
+ size_t previous_size = klass->GetObjectSize();
+ if (previous_size != 0) {
+ // Make sure that we didn't originally have an incorrect size.
+ CHECK_EQ(previous_size, size);
+ }
klass->SetObjectSize(size);
}
}
@@ -3961,9 +4041,8 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
if (resolved != NULL) {
return resolved;
}
- const DexFile::StringId& string_id = dex_file.GetStringId(string_idx);
- int32_t utf16_length = dex_file.GetStringLength(string_id);
- const char* utf8_data = dex_file.GetStringData(string_id);
+ uint32_t utf16_length;
+ const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
mirror::String* string = intern_table_->InternStrong(utf16_length, utf8_data);
dex_cache->SetResolvedString(string_idx, string);
return string;
@@ -4010,7 +4089,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
DCHECK(dex_cache != NULL);
// Check for hit in the dex cache.
mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx);
- if (resolved != NULL) {
+ if (resolved != NULL && !resolved->IsRuntimeMethod()) {
return resolved;
}
// Fail, get the declaring class.
@@ -4041,7 +4120,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
if (resolved == NULL) {
// Search by name, which works across dex files.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL));
+ const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect: // Fall-through.
case kStatic:
@@ -4071,7 +4150,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
// We failed to find the method which means either an access error, an incompatible class
// change, or no such method. First try to find the method among direct and virtual methods.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- std::string signature(dex_file.CreateMethodSignature(method_id.proto_idx_, NULL));
+ const Signature signature = dex_file.GetMethodSignature(method_id);
switch (type) {
case kDirect:
case kStatic:
@@ -4204,8 +4283,9 @@ mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file,
return NULL;
}
- const char* name = dex_file.GetFieldName(field_id);
- const char* type = dex_file.GetFieldTypeDescriptor(field_id);
+ StringPiece name(dex_file.StringDataByIdx(field_id.name_idx_));
+ StringPiece type(dex_file.StringDataByIdx(
+ dex_file.GetTypeId(field_id.type_idx_).descriptor_idx_));
resolved = klass->FindField(name, type);
if (resolved != NULL) {
dex_cache->SetResolvedField(field_idx, resolved);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 11ba78b36a..473370d90f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -51,6 +51,11 @@ typedef bool (ClassVisitor)(mirror::Class* c, void* arg);
class ClassLinker {
public:
+ // Interface method table size. Increasing this value reduces the chance of two interface methods
+ // colliding in the interface method table but increases the size of classes that implement
+ // (non-marker) interfaces.
+ static constexpr size_t kImtSize = 64;
+
// Creates the class linker by bootstrapping from dex files.
static ClassLinker* CreateFromCompiler(const std::vector<const DexFile*>& boot_class_path,
InternTable* intern_table)
@@ -215,7 +220,7 @@ class ClassLinker {
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RegisterOatFile(const OatFile& oat_file)
+ const OatFile* RegisterOatFile(const OatFile* oat_file)
LOCKS_EXCLUDED(dex_lock_);
const std::vector<const DexFile*>& GetBootClassPath() {
@@ -244,43 +249,37 @@ class ClassLinker {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Generate an oat file from a dex file
- bool GenerateOatFile(const std::string& dex_filename,
+ bool GenerateOatFile(const char* dex_filename,
int oat_fd,
- const std::string& oat_cache_filename);
+ const char* oat_cache_filename);
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
- const OatFile* FindOatFileFromOatLocation(const std::string& location)
+ const OatFile* FindOatFileFromOatLocation(const std::string& location,
+ std::string* error_msg)
LOCKS_EXCLUDED(dex_lock_);
- const OatFile* FindOatFileFromOatLocationLocked(const std::string& location)
- SHARED_LOCKS_REQUIRED(dex_lock_);
-
// Finds the oat file for a dex location, generating the oat file if
// it is missing or out of date. Returns the DexFile from within the
// created oat file.
- const DexFile* FindOrCreateOatFileForDexLocation(const std::string& dex_location,
+ const DexFile* FindOrCreateOatFileForDexLocation(const char* dex_location,
uint32_t dex_location_checksum,
- const std::string& oat_location)
- LOCKS_EXCLUDED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const DexFile* FindOrCreateOatFileForDexLocationLocked(const std::string& dex_location,
- uint32_t dex_location_checksum,
- const std::string& oat_location)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const char* oat_location,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Find a DexFile within an OatFile given a DexFile location. Note
// that this returns null if the location checksum of the DexFile
// does not match the OatFile.
- const DexFile* FindDexFileInOatFileFromDexLocation(const std::string& location,
- uint32_t location_checksum)
- LOCKS_EXCLUDED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const DexFile* FindDexFileInOatFileFromDexLocation(const char* location,
+ uint32_t location_checksum,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
// Returns true if oat file contains the dex file with the given location and checksum.
static bool VerifyOatFileChecksums(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const char* dex_location,
+ uint32_t dex_location_checksum,
+ std::string* error_msg);
// TODO: replace this with multiple methods that allocate the correct managed type.
template <class T>
@@ -346,6 +345,18 @@ class ClassLinker {
return quick_resolution_trampoline_;
}
+ const void* GetPortableImtConflictTrampoline() const {
+ return portable_imt_conflict_trampoline_;
+ }
+
+ const void* GetQuickImtConflictTrampoline() const {
+ return quick_imt_conflict_trampoline_;
+ }
+
+ InternTable* GetInternTable() const {
+ return intern_table_;
+ }
+
// Attempts to insert a class into a class table. Returns NULL if
// the class was inserted, otherwise returns an existing class with
// the same descriptor and ClassLoader.
@@ -430,8 +441,6 @@ class ClassLinker {
EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsDexFileRegisteredLocked(const DexFile& dex_file) const SHARED_LOCKS_REQUIRED(dex_lock_);
- void RegisterOatFileLocked(const OatFile& oat_file) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_);
bool InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_parents)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -493,22 +502,22 @@ class ClassLinker {
const OatFile* FindOpenedOatFileForDexFile(const DexFile& dex_file)
LOCKS_EXCLUDED(dex_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const OatFile* FindOpenedOatFileFromDexLocation(const std::string& dex_location,
+ const OatFile* FindOpenedOatFileFromDexLocation(const char* dex_location,
uint32_t dex_location_checksum)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_);
+ LOCKS_EXCLUDED(dex_lock);
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
- SHARED_LOCKS_REQUIRED(dex_lock_);
- const DexFile* FindDexFileInOatLocation(const std::string& dex_location,
+ LOCKS_EXCLUDED(dex_lock_);
+ const DexFile* FindDexFileInOatLocation(const char* dex_location,
uint32_t dex_location_checksum,
- const std::string& oat_location)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const char* oat_location,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_);
- const DexFile* VerifyAndOpenDexFileFromOatFile(const OatFile* oat_file,
- const std::string& dex_location,
- uint32_t dex_location_checksum)
- EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const DexFile* VerifyAndOpenDexFileFromOatFile(const std::string& oat_file_location,
+ const char* dex_location,
+ std::string* error_msg,
+ bool* open_failed)
+ LOCKS_EXCLUDED(dex_lock_);
mirror::ArtMethod* CreateProxyConstructor(Thread* self, SirtRef<mirror::Class>& klass,
mirror::Class* proxy_class)
@@ -616,6 +625,8 @@ class ClassLinker {
const void* portable_resolution_trampoline_;
const void* quick_resolution_trampoline_;
+ const void* portable_imt_conflict_trampoline_;
+ const void* quick_imt_conflict_trampoline_;
friend class ImageWriter; // for GetClassRoots
FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index bbc2877b73..a52b680260 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -152,7 +152,7 @@ class ClassLinkerTest : public CommonTest {
EXPECT_TRUE(method != NULL);
EXPECT_TRUE(method->GetClass() != NULL);
EXPECT_TRUE(mh.GetName() != NULL);
- EXPECT_TRUE(mh.GetSignature() != NULL);
+ EXPECT_TRUE(mh.GetSignature() != Signature::NoSignature());
EXPECT_TRUE(method->GetDexCacheStrings() != NULL);
EXPECT_TRUE(method->GetDexCacheResolvedMethods() != NULL);
@@ -340,8 +340,9 @@ class ClassLinkerTest : public CommonTest {
}
}
- static void TestRootVisitor(const mirror::Object* root, void*) {
+ static mirror::Object* TestRootVisitor(mirror::Object* root, void*) {
EXPECT_TRUE(root != NULL);
+ return root;
}
};
@@ -496,6 +497,7 @@ struct ClassOffsets : public CheckOffsets<mirror::Class> {
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, direct_methods_), "directMethods"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, ifields_), "iFields"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, iftable_), "ifTable"));
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, imtable_), "imTable"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, name_), "name"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, sfields_), "sFields"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Class, super_class_), "superClass"));
@@ -581,11 +583,11 @@ struct StringClassOffsets : public CheckOffsets<mirror::StringClass> {
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, ASCII_), "ASCII"));
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, CASE_INSENSITIVE_ORDER_), "CASE_INSENSITIVE_ORDER"));
- // alphabetical 64-bit
- offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID"));
-
// alphabetical 32-bit
offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, REPLACEMENT_CHAR_), "REPLACEMENT_CHAR"));
+
+ // alphabetical 64-bit
+ offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::StringClass, serialVersionUID_), "serialVersionUID"));
};
};
@@ -941,15 +943,16 @@ TEST_F(ClassLinkerTest, Interfaces) {
EXPECT_TRUE(K->IsAssignableFrom(B));
EXPECT_TRUE(J->IsAssignableFrom(B));
- mirror::ArtMethod* Ii = I->FindVirtualMethod("i", "()V");
- mirror::ArtMethod* Jj1 = J->FindVirtualMethod("j1", "()V");
- mirror::ArtMethod* Jj2 = J->FindVirtualMethod("j2", "()V");
- mirror::ArtMethod* Kj1 = K->FindInterfaceMethod("j1", "()V");
- mirror::ArtMethod* Kj2 = K->FindInterfaceMethod("j2", "()V");
- mirror::ArtMethod* Kk = K->FindInterfaceMethod("k", "()V");
- mirror::ArtMethod* Ai = A->FindVirtualMethod("i", "()V");
- mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", "()V");
- mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", "()V");
+ const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V");
+ mirror::ArtMethod* Ii = I->FindVirtualMethod("i", void_sig);
+ mirror::ArtMethod* Jj1 = J->FindVirtualMethod("j1", void_sig);
+ mirror::ArtMethod* Jj2 = J->FindVirtualMethod("j2", void_sig);
+ mirror::ArtMethod* Kj1 = K->FindInterfaceMethod("j1", void_sig);
+ mirror::ArtMethod* Kj2 = K->FindInterfaceMethod("j2", void_sig);
+ mirror::ArtMethod* Kk = K->FindInterfaceMethod("k", void_sig);
+ mirror::ArtMethod* Ai = A->FindVirtualMethod("i", void_sig);
+ mirror::ArtMethod* Aj1 = A->FindVirtualMethod("j1", void_sig);
+ mirror::ArtMethod* Aj2 = A->FindVirtualMethod("j2", void_sig);
ASSERT_TRUE(Ii != NULL);
ASSERT_TRUE(Jj1 != NULL);
ASSERT_TRUE(Jj2 != NULL);
@@ -994,7 +997,7 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) {
CHECK(dex_file != NULL);
mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader.get());
- mirror::ArtMethod* clinit = klass->FindDirectMethod("<clinit>", "()V");
+ mirror::ArtMethod* clinit = klass->FindClassInitializer();
mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;");
const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;");
ASSERT_TRUE(string_id != NULL);
diff --git a/runtime/common_test.h b/runtime/common_test.h
index dc1f5922d9..79fa680392 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -22,6 +22,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <fstream>
#include "../../external/icu4c/common/unicode/uvernum.h"
#include "base/macros.h"
@@ -152,6 +153,113 @@ class ScratchFile {
UniquePtr<File> file_;
};
+#if defined(__arm__)
+
+
+#include <signal.h>
+#include <asm/sigcontext.h>
+#include <asm/ucontext.h>
+
+
+// A signal handler called when have an illegal instruction. We record the fact in
+// a global boolean and then increment the PC in the signal context to return to
+// the next instruction. We know the instruction is an sdiv (4 bytes long).
+static void baddivideinst(int signo, siginfo *si, void *data) {
+ (void)signo;
+ (void)si;
+ struct ucontext *uc = (struct ucontext *)data;
+ struct sigcontext *sc = &uc->uc_mcontext;
+ sc->arm_r0 = 0; // set R0 to #0 to signal error
+ sc->arm_pc += 4; // skip offending instruction
+}
+
+// This is in arch/arm/arm_sdiv.S. It does the following:
+// mov r1,#1
+// sdiv r0,r1,r1
+// bx lr
+//
+// the result will be the value 1 if sdiv is supported. If it is not supported
+// a SIGILL signal will be raised and the signal handler (baddivideinst) called.
+// The signal handler sets r0 to #0 and then increments pc beyond the failed instruction.
+// Thus if the instruction is not supported, the result of this function will be #0
+
+extern "C" bool CheckForARMSDIVInstruction();
+
+static InstructionSetFeatures GuessInstructionFeatures() {
+ InstructionSetFeatures f;
+
+ // Uncomment this for processing of /proc/cpuinfo.
+ if (false) {
+ // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
+ // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
+ std::ifstream in("/proc/cpuinfo");
+ if (in) {
+ while (!in.eof()) {
+ std::string line;
+ std::getline(in, line);
+ if (!in.eof()) {
+ if (line.find("Features") != std::string::npos) {
+ if (line.find("idivt") != std::string::npos) {
+ f.SetHasDivideInstruction(true);
+ }
+ }
+ }
+ in.close();
+ }
+ } else {
+ LOG(INFO) << "Failed to open /proc/cpuinfo";
+ }
+ }
+
+ // See if have a sdiv instruction. Register a signal handler and try to execute
+ // an sdiv instruction. If we get a SIGILL then it's not supported. We can't use
+ // the /proc/cpuinfo method for this because Krait devices don't always put the idivt
+ // feature in the list.
+ struct sigaction sa, osa;
+ sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
+ sa.sa_sigaction = baddivideinst;
+ sigaction(SIGILL, &sa, &osa);
+
+ if (CheckForARMSDIVInstruction()) {
+ f.SetHasDivideInstruction(true);
+ }
+
+ // Restore the signal handler.
+ sigaction(SIGILL, &osa, NULL);
+
+ // Other feature guesses in here.
+ return f;
+}
+
+#endif
+
+// Given a set of instruction features from the build, parse it. The
+// input 'str' is a comma separated list of feature names. Parse it and
+// return the InstructionSetFeatures object.
+static InstructionSetFeatures ParseFeatureList(std::string str) {
+ LOG(INFO) << "Parsing features " << str;
+ InstructionSetFeatures result;
+ typedef std::vector<std::string> FeatureList;
+ FeatureList features;
+ Split(str, ',', features);
+ for (FeatureList::iterator i = features.begin(); i != features.end(); i++) {
+ std::string feature = Trim(*i);
+ if (feature == "default") {
+ // Nothing to do.
+ } else if (feature == "div") {
+ // Supports divide instruction.
+ result.SetHasDivideInstruction(true);
+ } else if (feature == "nodiv") {
+ // Turn off support for divide instruction.
+ result.SetHasDivideInstruction(false);
+ } else {
+ LOG(FATAL) << "Unknown instruction set feature: '" << feature << "'";
+ }
+ }
+ // Others...
+ return result;
+}
+
class CommonTest : public testing::Test {
public:
static void MakeExecutable(const mirror::ByteArray* code_array) {
@@ -282,16 +390,14 @@ class CommonTest : public testing::Test {
int mkdir_result = mkdir(dalvik_cache_.c_str(), 0700);
ASSERT_EQ(mkdir_result, 0);
- java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName(), GetLibCoreDexFileName());
+ std::string error_msg;
+ java_lang_dex_file_ = DexFile::Open(GetLibCoreDexFileName().c_str(),
+ GetLibCoreDexFileName().c_str(), &error_msg);
if (java_lang_dex_file_ == NULL) {
- LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "'\n";
- }
- conscrypt_file_ = DexFile::Open(GetConscryptFileName(), GetConscryptFileName());
- if (conscrypt_file_ == NULL) {
- LOG(FATAL) << "Could not open .dex file '" << GetConscryptFileName() << "'\n";
+ LOG(FATAL) << "Could not open .dex file '" << GetLibCoreDexFileName() << "': "
+ << error_msg << "\n";
}
boot_class_path_.push_back(java_lang_dex_file_);
- boot_class_path_.push_back(conscrypt_file_);
std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
@@ -316,8 +422,22 @@ class CommonTest : public testing::Test {
class_linker_ = runtime_->GetClassLinker();
InstructionSet instruction_set = kNone;
+
+ // take the default set of instruction features from the build if present
+ InstructionSetFeatures instruction_set_features =
+#ifdef ART_DEFAULT_INSTRUCTION_SET_FEATURES
+ ParseFeatureList(STRINGIFY(ART_DEFAULT_INSTRUCTION_SET_FEATURES));
+#else
+ ParseFeatureList("default");
+#endif
+
#if defined(__arm__)
instruction_set = kThumb2;
+ InstructionSetFeatures runtime_features = GuessInstructionFeatures();
+
+ // for ARM, do a runtime check to make sure that the features we are passed from
+ // the build match the features we actually determine at runtime.
+ ASSERT_EQ(instruction_set_features, runtime_features);
#elif defined(__mips__)
instruction_set = kMips;
#elif defined(__i386__)
@@ -331,9 +451,6 @@ class CommonTest : public testing::Test {
CompilerBackend compiler_backend = kQuick;
#endif
- if (!runtime_->HasResolutionMethod()) {
- runtime_->SetResolutionMethod(runtime_->CreateResolutionMethod());
- }
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
if (!runtime_->HasCalleeSaveMethod(type)) {
@@ -343,6 +460,7 @@ class CommonTest : public testing::Test {
}
class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set,
+ instruction_set_features,
true, new CompilerDriver::DescriptorSet,
2, true));
}
@@ -398,10 +516,6 @@ class CommonTest : public testing::Test {
return GetDexFileName("core-libart");
}
- std::string GetConscryptFileName() {
- return GetDexFileName("conscrypt");
- }
-
std::string GetDexFileName(const std::string& jar_prefix) {
if (IsHost()) {
const char* host_dir = getenv("ANDROID_HOST_OUT");
@@ -432,8 +546,9 @@ class CommonTest : public testing::Test {
filename += "art-test-dex-";
filename += name;
filename += ".jar";
- const DexFile* dex_file = DexFile::Open(filename, filename);
- CHECK(dex_file != NULL) << "Failed to open " << filename;
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg);
+ CHECK(dex_file != NULL) << "Failed to open '" << filename << "': " << error_msg;
CHECK_EQ(PROT_READ, dex_file->GetPermissions());
CHECK(dex_file->IsReadOnly());
opened_dex_files_.push_back(dex_file);
@@ -507,10 +622,12 @@ class CommonTest : public testing::Test {
void ReserveImageSpace() {
// Reserve where the image will be loaded up front so that other parts of test set up don't
// accidentally end up colliding with the fixed memory address when we need to load the image.
+ std::string error_msg;
image_reservation_.reset(MemMap::MapAnonymous("image reservation",
reinterpret_cast<byte*>(ART_BASE_ADDRESS),
(size_t)100 * 1024 * 1024, // 100MB
- PROT_NONE));
+ PROT_NONE, &error_msg));
+ CHECK(image_reservation_.get() != nullptr) << error_msg;
}
void UnreserveImageSpace() {
@@ -520,7 +637,6 @@ class CommonTest : public testing::Test {
std::string android_data_;
std::string dalvik_cache_;
const DexFile* java_lang_dex_file_; // owned by runtime_
- const DexFile* conscrypt_file_; // owned by runtime_
std::vector<const DexFile*> boot_class_path_;
UniquePtr<Runtime> runtime_;
// Owned by the runtime
@@ -575,7 +691,6 @@ class CheckJniAbortCatcher {
#else
#define TEST_DISABLED_FOR_PORTABLE()
#endif
-
} // namespace art
namespace std {
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 26ce5be1ec..0419dab0f8 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -230,6 +230,15 @@ void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char
va_end(args);
}
+// IOException
+
+void ThrowIOException(const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ ThrowException(NULL, "Ljava/io/IOException;", NULL, fmt, &args);
+ va_end(args);
+}
+
// LinkageError
void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...) {
@@ -265,7 +274,7 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c,
// NoSuchMethodError
void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name,
- const StringPiece& signature) {
+ const Signature& signature) {
std::ostringstream msg;
ClassHelper kh(c);
msg << "No " << type << " method " << name << signature
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 99c6343cdd..3164f308c1 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -22,113 +22,122 @@
namespace art {
namespace mirror {
-class ArtField;
-class ArtMethod;
-class Class;
-class Object;
+ class ArtField;
+ class ArtMethod;
+ class Class;
+ class Object;
} // namespace mirror
+class Signature;
class StringPiece;
class ThrowLocation;
// AbstractMethodError
void ThrowAbstractMethodError(const mirror::ArtMethod* method)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ArithmeticException
-void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowArithmeticExceptionDivideByZero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ArrayIndexOutOfBoundsException
void ThrowArrayIndexOutOfBoundsException(int index, int length)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ArrayStoreException
void ThrowArrayStoreException(const mirror::Class* element_class,
const mirror::Class* array_class)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ClassCircularityError
-void ThrowClassCircularityError(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowClassCircularityError(mirror::Class* c)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ClassCastException
void ThrowClassCastException(const mirror::Class* dest_type, const mirror::Class* src_type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// ClassFormatError
void ThrowClassFormatError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// IllegalAccessError
void ThrowIllegalAccessErrorClass(mirror::Class* referrer, mirror::Class* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
const mirror::ArtMethod* caller,
const mirror::ArtMethod* called,
InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::ArtMethod* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::ArtField* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessErrorFinalField(const mirror::ArtMethod* referrer,
mirror::ArtField* accessed)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// IllegalArgumentException
void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// IncompatibleClassChangeError
void ThrowIncompatibleClassChangeError(InvokeType expected_type, InvokeType found_type,
mirror::ArtMethod* method,
const mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(const mirror::ArtMethod* interface_method,
mirror::Object* this_object,
const mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIncompatibleClassChangeErrorField(const mirror::ArtField* resolved_field, bool is_static,
const mirror::ArtMethod* referrer)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowIncompatibleClassChangeError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
+
+// IOException
+
+void ThrowIOException(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)))
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// LinkageError
void ThrowLinkageError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// NegativeArraySizeException
-void ThrowNegativeArraySizeException(int size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowNegativeArraySizeException(int size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
-void ThrowNegativeArraySizeException(const char* msg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+void ThrowNegativeArraySizeException(const char* msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// NoSuchFieldError
@@ -140,46 +149,46 @@ void ThrowNoSuchFieldError(const StringPiece& scope, mirror::Class* c,
// NoSuchMethodError
void ThrowNoSuchMethodError(InvokeType type, mirror::Class* c, const StringPiece& name,
- const StringPiece& signature)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const Signature& signature)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNoSuchMethodError(uint32_t method_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// NullPointerException
void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location,
mirror::ArtField* field,
bool is_read)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
uint32_t method_idx,
InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
mirror::ArtMethod* method,
InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// RuntimeException
void ThrowRuntimeException(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
// VerifyError
void ThrowVerifyError(const mirror::Class* referrer, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
} // namespace art
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 73f83a2f12..bdcf6ac6ab 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -674,15 +674,15 @@ JDWP::JdwpError Dbg::GetMonitorInfo(JDWP::ObjectId object_id, JDWP::ExpandBuf* r
Locks::mutator_lock_->ExclusiveUnlock(self);
Locks::mutator_lock_->SharedLock(self);
- if (monitor_info.owner != NULL) {
- expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner->GetPeer()));
+ if (monitor_info.owner_ != NULL) {
+ expandBufAddObjectId(reply, gRegistry->Add(monitor_info.owner_->GetPeer()));
} else {
expandBufAddObjectId(reply, gRegistry->Add(NULL));
}
- expandBufAdd4BE(reply, monitor_info.entry_count);
- expandBufAdd4BE(reply, monitor_info.waiters.size());
- for (size_t i = 0; i < monitor_info.waiters.size(); ++i) {
- expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters[i]->GetPeer()));
+ expandBufAdd4BE(reply, monitor_info.entry_count_);
+ expandBufAdd4BE(reply, monitor_info.waiters_.size());
+ for (size_t i = 0; i < monitor_info.waiters_.size(); ++i) {
+ expandBufAddObjectId(reply, gRegistry->Add(monitor_info.waiters_[i]->GetPeer()));
}
return JDWP::ERR_NONE;
}
@@ -928,13 +928,13 @@ JDWP::JdwpError Dbg::GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf*
return JDWP::ERR_NONE;
}
-JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string& signature) {
+JDWP::JdwpError Dbg::GetSignature(JDWP::RefTypeId class_id, std::string* signature) {
JDWP::JdwpError status;
mirror::Class* c = DecodeClass(class_id, status);
if (c == NULL) {
return status;
}
- signature = ClassHelper(c).GetDescriptor();
+ *signature = ClassHelper(c).GetDescriptor();
return JDWP::ERR_NONE;
}
@@ -1065,8 +1065,8 @@ JDWP::JdwpError Dbg::SetArrayElements(JDWP::ObjectId array_id, int offset, int c
LOG(WARNING) << __FUNCTION__ << " access out of bounds: offset=" << offset << "; count=" << count;
return JDWP::ERR_INVALID_LENGTH;
}
- std::string descriptor(ClassHelper(dst->GetClass()).GetDescriptor());
- JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor.c_str() + 1);
+ const char* descriptor = ClassHelper(dst->GetClass()).GetDescriptor();
+ JDWP::JdwpTag tag = BasicTagFromDescriptor(descriptor + 1);
if (IsPrimitiveTag(tag)) {
size_t width = GetTagWidth(tag);
@@ -1287,7 +1287,7 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g
MethodHelper mh(m);
expandBufAddMethodId(pReply, ToMethodId(m));
expandBufAddUtf8String(pReply, mh.GetName());
- expandBufAddUtf8String(pReply, mh.GetSignature());
+ expandBufAddUtf8String(pReply, mh.GetSignature().ToString());
if (with_generic) {
static const char genericSignature[1] = "";
expandBufAddUtf8String(pReply, genericSignature);
@@ -1935,7 +1935,8 @@ JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspen
}
// Suspend thread to build stack trace.
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer.get(), request_suspension, &timed_out);
+ Thread* thread = ThreadList::SuspendThreadByPeer(peer.get(), request_suspension, true,
+ &timed_out);
if (thread != NULL) {
return JDWP::ERR_NONE;
} else if (timed_out) {
@@ -2287,7 +2288,8 @@ void Dbg::PostClassPrepare(mirror::Class* c) {
// since the class may not yet be verified.
int state = JDWP::CS_VERIFIED | JDWP::CS_PREPARED;
JDWP::JdwpTypeTag tag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
- gJdwpState->PostClassPrepare(tag, gRegistry->Add(c), ClassHelper(c).GetDescriptor(), state);
+ gJdwpState->PostClassPrepare(tag, gRegistry->Add(c),
+ ClassHelper(c).GetDescriptor(), state);
}
void Dbg::UpdateDebugger(Thread* thread, mirror::Object* this_object,
@@ -2411,7 +2413,8 @@ class ScopedThreadSuspension {
soa.Self()->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
jobject thread_peer = gRegistry->GetJObject(thread_id);
bool timed_out;
- Thread* suspended_thread = Thread::SuspendForDebugger(thread_peer, true, &timed_out);
+ Thread* suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true,
+ &timed_out);
CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kWaitingForDebuggerSuspension);
if (suspended_thread == NULL) {
// Thread terminated from under us while suspending.
@@ -3011,7 +3014,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
if (type == CHUNK_TYPE("THDE")) {
uint8_t buf[4];
- JDWP::Set4BE(&buf[0], t->GetThinLockId());
+ JDWP::Set4BE(&buf[0], t->GetThreadId());
Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
} else {
CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
@@ -3021,7 +3024,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) {
const jchar* chars = (name.get() != NULL) ? name->GetCharArray()->GetData() : NULL;
std::vector<uint8_t> bytes;
- JDWP::Append4BE(bytes, t->GetThinLockId());
+ JDWP::Append4BE(bytes, t->GetThreadId());
JDWP::AppendUtf16BE(bytes, chars, char_count);
CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
Dbg::DdmSendChunk(type, bytes);
@@ -3486,7 +3489,9 @@ void Dbg::SetAllocTrackingEnabled(bool enabled) {
recent_allocation_records_ = new AllocRecord[gAllocRecordMax];
CHECK(recent_allocation_records_ != NULL);
}
+ Runtime::Current()->InstrumentQuickAllocEntryPoints();
} else {
+ Runtime::Current()->UninstrumentQuickAllocEntryPoints();
delete[] recent_allocation_records_;
recent_allocation_records_ = NULL;
}
@@ -3542,7 +3547,7 @@ void Dbg::RecordAllocation(mirror::Class* type, size_t byte_count) {
AllocRecord* record = &recent_allocation_records_[gAllocRecordHead];
record->type = type;
record->byte_count = byte_count;
- record->thin_lock_id = self->GetThinLockId();
+ record->thin_lock_id = self->GetThreadId();
// Fill in the stack trace.
AllocRecordStackVisitor visitor(self, record);
diff --git a/runtime/debugger.h b/runtime/debugger.h
index d0fe445df1..8574a3308f 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -149,7 +149,7 @@ class Dbg {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetReferenceType(JDWP::ObjectId object_id, JDWP::ExpandBuf* pReply)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static JDWP::JdwpError GetSignature(JDWP::RefTypeId ref_type_id, std::string& signature)
+ static JDWP::JdwpError GetSignature(JDWP::RefTypeId ref_type_id, std::string* signature)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static JDWP::JdwpError GetSourceFile(JDWP::RefTypeId ref_type_id, std::string& source_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index dee80269d6..3b2135c2cd 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -29,19 +29,89 @@ inline int32_t DexFile::GetStringLength(const StringId& string_id) const {
return DecodeUnsignedLeb128(&ptr);
}
-inline const char* DexFile::GetStringDataAndLength(const StringId& string_id, uint32_t* length) const {
- DCHECK(length != NULL) << GetLocation();
+inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_id,
+ uint32_t* utf16_length) const {
+ DCHECK(utf16_length != NULL) << GetLocation();
const byte* ptr = begin_ + string_id.string_data_off_;
- *length = DecodeUnsignedLeb128(&ptr);
+ *utf16_length = DecodeUnsignedLeb128(&ptr);
return reinterpret_cast<const char*>(ptr);
}
+inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const {
+ return Signature(this, GetProtoId(method_id.proto_idx_));
+}
+
inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) {
const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_];
return reinterpret_cast<const TryItem*>
(RoundUp(reinterpret_cast<uint32_t>(insns_end_), 4)) + offset;
}
+static inline bool DexFileStringEquals(const DexFile* df1, uint32_t sidx1,
+ const DexFile* df2, uint32_t sidx2) {
+ uint32_t s1_len; // Note: utf16 length != mutf8 length.
+ const char* s1_data = df1->StringDataAndUtf16LengthByIdx(sidx1, &s1_len);
+ uint32_t s2_len;
+ const char* s2_data = df2->StringDataAndUtf16LengthByIdx(sidx2, &s2_len);
+ return (s1_len == s2_len) && (strcmp(s1_data, s2_data) == 0);
+}
+
+inline bool Signature::operator==(const Signature& rhs) const {
+ if (dex_file_ == nullptr) {
+ return rhs.dex_file_ == nullptr;
+ }
+ if (rhs.dex_file_ == nullptr) {
+ return false;
+ }
+ if (dex_file_ == rhs.dex_file_) {
+ return proto_id_ == rhs.proto_id_;
+ }
+ uint32_t lhs_shorty_len; // For a shorty utf16 length == mutf8 length.
+ const char* lhs_shorty_data = dex_file_->StringDataAndUtf16LengthByIdx(proto_id_->shorty_idx_,
+ &lhs_shorty_len);
+ StringPiece lhs_shorty(lhs_shorty_data, lhs_shorty_len);
+ {
+ uint32_t rhs_shorty_len;
+ const char* rhs_shorty_data =
+ rhs.dex_file_->StringDataAndUtf16LengthByIdx(rhs.proto_id_->shorty_idx_,
+ &rhs_shorty_len);
+ StringPiece rhs_shorty(rhs_shorty_data, rhs_shorty_len);
+ if (lhs_shorty != rhs_shorty) {
+ return false; // Shorty mismatch.
+ }
+ }
+ if (lhs_shorty[0] == 'L') {
+ const DexFile::TypeId& return_type_id = dex_file_->GetTypeId(proto_id_->return_type_idx_);
+ const DexFile::TypeId& rhs_return_type_id =
+ rhs.dex_file_->GetTypeId(rhs.proto_id_->return_type_idx_);
+ if (!DexFileStringEquals(dex_file_, return_type_id.descriptor_idx_,
+ rhs.dex_file_, rhs_return_type_id.descriptor_idx_)) {
+ return false; // Return type mismatch.
+ }
+ }
+ if (lhs_shorty.find('L', 1) != StringPiece::npos) {
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
+ // Both lists are empty or have contents, or else shorty is broken.
+ DCHECK_EQ(params == nullptr, rhs_params == nullptr);
+ if (params != nullptr) {
+ uint32_t params_size = params->Size();
+ DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
+ for (uint32_t i = 0; i < params_size; ++i) {
+ const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_);
+ const DexFile::TypeId& rhs_param_id =
+ rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_);
+ if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_,
+ rhs.dex_file_, rhs_param_id.descriptor_idx_)) {
+ return false; // Parameter type mismatch.
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
} // namespace art
#endif // ART_RUNTIME_DEX_FILE_INL_H_
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 9034628486..7e09a489be 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -38,7 +38,7 @@
#include "safe_map.h"
#include "thread.h"
#include "UniquePtr.h"
-#include "utf.h"
+#include "utf-inl.h"
#include "utils.h"
#include "well_known_classes.h"
#include "zip_archive.h"
@@ -62,72 +62,77 @@ DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor,
reinterpret_cast<const DexFile::ClassDef*>(NULL));
}
-int OpenAndReadMagic(const std::string& filename, uint32_t* magic) {
+static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
CHECK(magic != NULL);
- int fd = open(filename.c_str(), O_RDONLY, 0);
+ int fd = open(filename, O_RDONLY, 0);
if (fd == -1) {
- PLOG(WARNING) << "Unable to open '" << filename << "'";
+ *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno));
return -1;
}
int n = TEMP_FAILURE_RETRY(read(fd, magic, sizeof(*magic)));
if (n != sizeof(*magic)) {
- PLOG(ERROR) << "Failed to find magic in '" << filename << "'";
+ *error_msg = StringPrintf("Failed to find magic in '%s'", filename);
return -1;
}
if (lseek(fd, 0, SEEK_SET) != 0) {
- PLOG(ERROR) << "Failed to seek to beginning of file '" << filename << "'";
+ *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename,
+ strerror(errno));
return -1;
}
return fd;
}
-bool DexFile::GetChecksum(const std::string& filename, uint32_t* checksum) {
+bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
CHECK(checksum != NULL);
uint32_t magic;
- int fd = OpenAndReadMagic(filename, &magic);
+ int fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd == -1) {
+ DCHECK(!error_msg->empty());
return false;
}
if (IsZipMagic(magic)) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd));
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, filename, error_msg));
if (zip_archive.get() == NULL) {
+ *error_msg = StringPrintf("Failed to open zip archive '%s'", filename);
return false;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(kClassesDex));
if (zip_entry.get() == NULL) {
- LOG(ERROR) << "Zip archive '" << filename << "' doesn't contain " << kClassesDex;
+ *error_msg = StringPrintf("Zip archive '%s' doesn\'t contain %s", filename, kClassesDex);
return false;
}
*checksum = zip_entry->GetCrc32();
return true;
}
if (IsDexMagic(magic)) {
- UniquePtr<const DexFile> dex_file(DexFile::OpenFile(fd, filename, false));
+ UniquePtr<const DexFile> dex_file(DexFile::OpenFile(fd, filename, false, error_msg));
if (dex_file.get() == NULL) {
return false;
}
*checksum = dex_file->GetHeader().checksum_;
return true;
}
- LOG(ERROR) << "Expected valid zip or dex file: " << filename;
+ *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
return false;
}
-const DexFile* DexFile::Open(const std::string& filename,
- const std::string& location) {
+const DexFile* DexFile::Open(const char* filename,
+ const char* location,
+ std::string* error_msg) {
uint32_t magic;
- int fd = OpenAndReadMagic(filename, &magic);
+ int fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd == -1) {
+ DCHECK(!error_msg->empty());
return NULL;
}
if (IsZipMagic(magic)) {
- return DexFile::OpenZip(fd, location);
+ return DexFile::OpenZip(fd, location, error_msg);
}
if (IsDexMagic(magic)) {
- return DexFile::OpenFile(fd, location, true);
+ return DexFile::OpenFile(fd, location, true, error_msg);
}
- LOG(ERROR) << "Expected valid zip or dex file: " << filename;
- return NULL;
+ *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
+ return nullptr;
}
int DexFile::GetPermissions() const {
@@ -160,46 +165,48 @@ bool DexFile::DisableWrite() const {
}
}
-const DexFile* DexFile::OpenFile(int fd,
- const std::string& location,
- bool verify) {
- CHECK(!location.empty());
+const DexFile* DexFile::OpenFile(int fd, const char* location, bool verify,
+ std::string* error_msg) {
+ CHECK(location != nullptr);
struct stat sbuf;
memset(&sbuf, 0, sizeof(sbuf));
if (fstat(fd, &sbuf) == -1) {
- PLOG(ERROR) << "fstat \"" << location << "\" failed";
+ *error_msg = StringPrintf("DexFile: fstat \'%s\' failed: %s", location, strerror(errno));
close(fd);
- return NULL;
+ return nullptr;
}
if (S_ISDIR(sbuf.st_mode)) {
- LOG(ERROR) << "attempt to mmap directory \"" << location << "\"";
- return NULL;
+ *error_msg = StringPrintf("Attempt to mmap directory '%s'", location);
+ return nullptr;
}
size_t length = sbuf.st_size;
- UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0));
- if (map.get() == NULL) {
- LOG(ERROR) << "mmap \"" << location << "\" failed";
+ UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ, MAP_PRIVATE, fd, 0, location,
+ error_msg));
+ if (map.get() == nullptr) {
+ DCHECK(!error_msg->empty());
close(fd);
- return NULL;
+ return nullptr;
}
close(fd);
if (map->Size() < sizeof(DexFile::Header)) {
- LOG(ERROR) << "Failed to open dex file '" << location << "' that is too short to have a header";
- return NULL;
+ *error_msg = StringPrintf(
+ "DexFile: failed to open dex file \'%s\' that is too short to have a header", location);
+ return nullptr;
}
const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
- const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release());
- if (dex_file == NULL) {
- LOG(ERROR) << "Failed to open dex file '" << location << "' from memory";
- return NULL;
+ const DexFile* dex_file = OpenMemory(location, dex_header->checksum_, map.release(), error_msg);
+ if (dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location,
+ error_msg->c_str());
+ return nullptr;
}
- if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size())) {
- LOG(ERROR) << "Failed to verify dex file '" << location << "'";
- return NULL;
+ if (verify && !DexFileVerifier::Verify(dex_file, dex_file->Begin(), dex_file->Size(), location,
+ error_msg)) {
+ return nullptr;
}
return dex_file;
@@ -207,49 +214,55 @@ const DexFile* DexFile::OpenFile(int fd,
const char* DexFile::kClassesDex = "classes.dex";
-const DexFile* DexFile::OpenZip(int fd, const std::string& location) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd));
- if (zip_archive.get() == NULL) {
- LOG(ERROR) << "Failed to open " << location << " when looking for classes.dex";
- return NULL;
+const DexFile* DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg) {
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg));
+ if (zip_archive.get() == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
- return DexFile::Open(*zip_archive.get(), location);
+ return DexFile::Open(*zip_archive.get(), location, error_msg);
}
const DexFile* DexFile::OpenMemory(const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map) {
+ MemMap* mem_map,
+ std::string* error_msg) {
return OpenMemory(mem_map->Begin(),
mem_map->Size(),
location,
location_checksum,
- mem_map);
+ mem_map,
+ error_msg);
}
-const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location) {
+const DexFile* DexFile::Open(const ZipArchive& zip_archive, const std::string& location,
+ std::string* error_msg) {
CHECK(!location.empty());
UniquePtr<ZipEntry> zip_entry(zip_archive.Find(kClassesDex));
if (zip_entry.get() == NULL) {
- LOG(ERROR) << "Failed to find classes.dex within '" << location << "'";
- return NULL;
+ *error_msg = StringPrintf("Failed to find classes.dex within '%s'", location.c_str());
+ return nullptr;
}
- UniquePtr<MemMap> map(zip_entry->ExtractToMemMap(kClassesDex));
+ UniquePtr<MemMap> map(zip_entry->ExtractToMemMap(kClassesDex, error_msg));
if (map.get() == NULL) {
- LOG(ERROR) << "Failed to extract '" << kClassesDex << "' from '" << location << "'";
- return NULL;
+ *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", kClassesDex, location.c_str(),
+ error_msg->c_str());
+ return nullptr;
}
- UniquePtr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release()));
- if (dex_file.get() == NULL) {
- LOG(ERROR) << "Failed to open dex file '" << location << "' from memory";
- return NULL;
+ UniquePtr<const DexFile> dex_file(OpenMemory(location, zip_entry->GetCrc32(), map.release(),
+ error_msg));
+ if (dex_file.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
+ error_msg->c_str());
+ return nullptr;
}
- if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size())) {
- LOG(ERROR) << "Failed to verify dex file '" << location << "'";
- return NULL;
+ if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(),
+ location.c_str(), error_msg)) {
+ return nullptr;
}
if (!dex_file->DisableWrite()) {
- LOG(ERROR) << "Failed to make dex file read only '" << location << "'";
- return NULL;
+ *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
+ return nullptr;
}
CHECK(dex_file->IsReadOnly()) << location;
return dex_file.release();
@@ -259,11 +272,11 @@ const DexFile* DexFile::OpenMemory(const byte* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map) {
+ MemMap* mem_map, std::string* error_msg) {
CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned
UniquePtr<DexFile> dex_file(new DexFile(base, size, location, location_checksum, mem_map));
- if (!dex_file->Init()) {
- return NULL;
+ if (!dex_file->Init(error_msg)) {
+ return nullptr;
} else {
return dex_file.release();
}
@@ -276,9 +289,9 @@ DexFile::~DexFile() {
// the global reference table is otherwise empty!
}
-bool DexFile::Init() {
+bool DexFile::Init(std::string* error_msg) {
InitMembers();
- if (!CheckMagicAndVersion()) {
+ if (!CheckMagicAndVersion(error_msg)) {
return false;
}
return true;
@@ -296,22 +309,26 @@ void DexFile::InitMembers() {
class_defs_ = reinterpret_cast<const ClassDef*>(b + h->class_defs_off_);
}
-bool DexFile::CheckMagicAndVersion() const {
+bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
CHECK(header_->magic_ != NULL) << GetLocation();
if (!IsMagicValid(header_->magic_)) {
- LOG(ERROR) << "Unrecognized magic number in " << GetLocation() << ":"
+ std::ostringstream oss;
+ oss << "Unrecognized magic number in " << GetLocation() << ":"
<< " " << header_->magic_[0]
<< " " << header_->magic_[1]
<< " " << header_->magic_[2]
<< " " << header_->magic_[3];
+ *error_msg = oss.str();
return false;
}
if (!IsVersionValid(header_->magic_)) {
- LOG(ERROR) << "Unrecognized version number in " << GetLocation() << ":"
+ std::ostringstream oss;
+ oss << "Unrecognized version number in " << GetLocation() << ":"
<< " " << header_->magic_[4]
<< " " << header_->magic_[5]
<< " " << header_->magic_[6]
<< " " << header_->magic_[7];
+ *error_msg = oss.str();
return false;
}
return true;
@@ -440,9 +457,8 @@ const DexFile::StringId* DexFile::FindStringId(const char* string) const {
int32_t hi = NumStringIds() - 1;
while (hi >= lo) {
int32_t mid = (hi + lo) / 2;
- uint32_t length;
const DexFile::StringId& str_id = GetStringId(mid);
- const char* str = GetStringDataAndLength(str_id, &length);
+ const char* str = GetStringData(str_id);
int compare = CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(string, str);
if (compare > 0) {
lo = mid + 1;
@@ -460,9 +476,8 @@ const DexFile::StringId* DexFile::FindStringId(const uint16_t* string) const {
int32_t hi = NumStringIds() - 1;
while (hi >= lo) {
int32_t mid = (hi + lo) / 2;
- uint32_t length;
const DexFile::StringId& str_id = GetStringId(mid);
- const char* str = GetStringDataAndLength(str_id, &length);
+ const char* str = GetStringData(str_id);
int compare = CompareModifiedUtf8ToUtf16AsCodePointValues(str, string);
if (compare > 0) {
lo = mid + 1;
@@ -528,8 +543,8 @@ const DexFile::ProtoId* DexFile::FindProtoId(uint16_t return_type_idx,
}
// Given a signature place the type ids into the given vector
-bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* param_type_idxs,
- const std::string& signature) const {
+bool DexFile::CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
+ std::vector<uint16_t>* param_type_idxs) const {
if (signature[0] != '(') {
return false;
}
@@ -543,6 +558,7 @@ bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* p
process_return = true;
continue;
}
+ // TODO: avoid building a string.
std::string descriptor;
descriptor += c;
while (c == '[') { // process array prefix
@@ -582,35 +598,18 @@ bool DexFile::CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* p
return false; // failed to correctly parse return type
}
-// Materializes the method descriptor for a method prototype. Method
-// descriptors are not stored directly in the dex file. Instead, one
-// must assemble the descriptor from references in the prototype.
-std::string DexFile::CreateMethodSignature(uint32_t proto_idx, int32_t* unicode_length) const {
- const ProtoId& proto_id = GetProtoId(proto_idx);
- std::string descriptor;
- descriptor.push_back('(');
- const TypeList* type_list = GetProtoParameters(proto_id);
- size_t parameter_length = 0;
- if (type_list != NULL) {
- // A non-zero number of arguments. Append the type names.
- for (size_t i = 0; i < type_list->Size(); ++i) {
- const TypeItem& type_item = type_list->GetTypeItem(i);
- uint32_t type_idx = type_item.type_idx_;
- uint32_t type_length;
- const char* name = StringByTypeIdx(type_idx, &type_length);
- parameter_length += type_length;
- descriptor.append(name);
- }
+const Signature DexFile::CreateSignature(const StringPiece& signature) const {
+ uint16_t return_type_idx;
+ std::vector<uint16_t> param_type_indices;
+ bool success = CreateTypeList(signature, &return_type_idx, &param_type_indices);
+ if (!success) {
+ return Signature::NoSignature();
}
- descriptor.push_back(')');
- uint32_t return_type_idx = proto_id.return_type_idx_;
- uint32_t return_type_length;
- const char* name = StringByTypeIdx(return_type_idx, &return_type_length);
- descriptor.append(name);
- if (unicode_length != NULL) {
- *unicode_length = parameter_length + return_type_length + 2; // 2 for ( and )
+ const ProtoId* proto_id = FindProtoId(return_type_idx, param_type_indices);
+ if (proto_id == NULL) {
+ return Signature::NoSignature();
}
- return descriptor;
+ return Signature(this, *proto_id);
}
int32_t DexFile::GetLineNumFromPC(const mirror::ArtMethod* method, uint32_t rel_pc) const {
@@ -856,6 +855,30 @@ bool DexFile::LineNumForPcCb(void* raw_context, uint32_t address, uint32_t line_
}
}
+std::string Signature::ToString() const {
+ if (dex_file_ == nullptr) {
+ CHECK(proto_id_ == nullptr);
+ return "<no signature>";
+ }
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ std::string result;
+ if (params == nullptr) {
+ result += "()";
+ } else {
+ result += "(";
+ for (uint32_t i = 0; i < params->Size(); ++i) {
+ result += dex_file_->StringByTypeIdx(params->GetTypeItem(i).type_idx_);
+ }
+ result += ")";
+ }
+ result += dex_file_->StringByTypeIdx(proto_id_->return_type_idx_);
+ return result;
+}
+
+std::ostream& operator<<(std::ostream& os, const Signature& sig) {
+ return os << sig.ToString();
+}
+
// Decodes the header section from the class data bytes.
void ClassDataItemIterator::ReadClassDataHeader() {
CHECK(ptr_pos_ != NULL);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cef4ce4b87..a9c24e66c1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -42,6 +42,8 @@ namespace mirror {
class DexCache;
} // namespace mirror
class ClassLinker;
+class Signature;
+class StringPiece;
class ZipArchive;
// TODO: move all of the macro functionality into the DexCache class.
@@ -348,22 +350,22 @@ class DexFile {
// For .dex files, this is the header checksum.
// For zip files, this is the classes.dex zip entry CRC32 checksum.
// Return true if the checksum could be found, false otherwise.
- static bool GetChecksum(const std::string& filename, uint32_t* checksum)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
// Opens .dex file, guessing the container format based on file extension
- static const DexFile* Open(const std::string& filename,
- const std::string& location);
+ static const DexFile* Open(const char* filename, const char* location, std::string* error_msg);
// Opens .dex file, backed by existing memory
static const DexFile* Open(const uint8_t* base, size_t size,
const std::string& location,
- uint32_t location_checksum) {
- return OpenMemory(base, size, location, location_checksum, NULL);
+ uint32_t location_checksum,
+ std::string* error_msg) {
+ return OpenMemory(base, size, location, location_checksum, NULL, error_msg);
}
// Opens .dex file from the classes.dex in a zip archive
- static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location);
+ static const DexFile* Open(const ZipArchive& zip_archive, const std::string& location,
+ std::string* error_msg);
// Closes a .dex file.
virtual ~DexFile();
@@ -416,27 +418,29 @@ class DexFile {
int32_t GetStringLength(const StringId& string_id) const;
- // Returns a pointer to the UTF-8 string data referred to by the given string_id.
- const char* GetStringDataAndLength(const StringId& string_id, uint32_t* length) const;
+ // Returns a pointer to the UTF-8 string data referred to by the given string_id as well as the
+ // length of the string when decoded as a UTF-16 string. Note the UTF-16 length is not the same
+ // as the string length of the string data.
+ const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const;
const char* GetStringData(const StringId& string_id) const {
- uint32_t length;
- return GetStringDataAndLength(string_id, &length);
+ uint32_t ignored;
+ return GetStringDataAndUtf16Length(string_id, &ignored);
}
- // return the UTF-8 encoded string with the specified string_id index
- const char* StringDataAndLengthByIdx(uint32_t idx, uint32_t* unicode_length) const {
+ // Index version of GetStringDataAndUtf16Length.
+ const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const {
if (idx == kDexNoIndex) {
- *unicode_length = 0;
+ *utf16_length = 0;
return NULL;
}
const StringId& string_id = GetStringId(idx);
- return GetStringDataAndLength(string_id, unicode_length);
+ return GetStringDataAndUtf16Length(string_id, utf16_length);
}
const char* StringDataByIdx(uint32_t idx) const {
uint32_t unicode_length;
- return StringDataAndLengthByIdx(idx, &unicode_length);
+ return StringDataAndUtf16LengthByIdx(idx, &unicode_length);
}
// Looks up a string id for a given modified utf8 string.
@@ -468,7 +472,7 @@ class DexFile {
// Get the descriptor string associated with a given type index.
const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const {
const TypeId& type_id = GetTypeId(idx);
- return StringDataAndLengthByIdx(type_id.descriptor_idx_, unicode_length);
+ return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length);
}
const char* StringByTypeIdx(uint32_t idx) const {
@@ -558,10 +562,8 @@ class DexFile {
return GetProtoId(method_id.proto_idx_);
}
- // Returns the signature of a method id.
- const std::string GetMethodSignature(const MethodId& method_id) const {
- return CreateMethodSignature(method_id.proto_idx_, NULL);
- }
+ // Returns a representation of the signature of a method id.
+ const Signature GetMethodSignature(const MethodId& method_id) const;
// Returns the name of a method id.
const char* GetMethodName(const MethodId& method_id) const {
@@ -573,7 +575,8 @@ class DexFile {
return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_);
}
const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const {
- return StringDataAndLengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
+ // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters.
+ return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
}
// Returns the number of class definitions in the .dex file.
size_t NumClassDefs() const {
@@ -655,15 +658,16 @@ class DexFile {
}
// Looks up a proto id for a given return type and signature type list
- const ProtoId* FindProtoId(uint16_t return_type_id,
+ const ProtoId* FindProtoId(uint16_t return_type_idx,
const std::vector<uint16_t>& signature_type_idxs_) const;
// Given a signature place the type ids into the given vector, returns true on success
- bool CreateTypeList(uint16_t* return_type_idx, std::vector<uint16_t>* param_type_idxs,
- const std::string& signature) const;
+ bool CreateTypeList(const StringPiece& signature, uint16_t* return_type_idx,
+ std::vector<uint16_t>* param_type_idxs) const;
- // Given a proto_idx decode the type list and return type into a method signature
- std::string CreateMethodSignature(uint32_t proto_idx, int32_t* unicode_length) const;
+ // Create a Signature from the given string signature or return Signature::NoSignature if not
+ // possible.
+ const Signature CreateSignature(const StringPiece& signature) const;
// Returns the short form method descriptor for the given prototype.
const char* GetShorty(uint32_t proto_idx) const {
@@ -817,24 +821,24 @@ class DexFile {
private:
// Opens a .dex file
- static const DexFile* OpenFile(int fd,
- const std::string& location,
- bool verify);
+ static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg);
// Opens a dex file from within a .jar, .zip, or .apk file
- static const DexFile* OpenZip(int fd, const std::string& location);
+ static const DexFile* OpenZip(int fd, const std::string& location, std::string* error_msg);
// Opens a .dex file at the given address backed by a MemMap
static const DexFile* OpenMemory(const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map);
+ MemMap* mem_map,
+ std::string* error_msg);
// Opens a .dex file at the given address, optionally backed by a MemMap
static const DexFile* OpenMemory(const byte* dex_file,
size_t size,
const std::string& location,
uint32_t location_checksum,
- MemMap* mem_map);
+ MemMap* mem_map,
+ std::string* error_msg);
DexFile(const byte* base, size_t size,
const std::string& location,
@@ -858,13 +862,13 @@ class DexFile {
}
// Top-level initializer that calls other Init methods.
- bool Init();
+ bool Init(std::string* error_msg);
// Caches pointers into to the various file sections.
void InitMembers();
// Returns true if the header magic and version numbers are of the expected values.
- bool CheckMagicAndVersion() const;
+ bool CheckMagicAndVersion(std::string* error_msg) const;
void DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32_t method_idx,
DexDebugNewPositionCb position_cb, DexDebugNewLocalCb local_cb,
@@ -940,6 +944,39 @@ class DexFileParameterIterator {
DISALLOW_IMPLICIT_CONSTRUCTORS(DexFileParameterIterator);
};
+// Abstract the signature of a method.
+class Signature {
+ public:
+ std::string ToString() const;
+
+ static Signature NoSignature() {
+ return Signature();
+ }
+
+ bool operator==(const Signature& rhs) const;
+ bool operator!=(const Signature& rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator==(const StringPiece& rhs) const {
+ // TODO: Avoid temporary string allocation.
+ return ToString() == rhs;
+ }
+
+ private:
+ Signature(const DexFile* dex, const DexFile::ProtoId& proto) : dex_file_(dex), proto_id_(&proto) {
+ }
+
+ Signature() : dex_file_(nullptr), proto_id_(nullptr) {
+ }
+
+ friend class DexFile;
+
+ const DexFile* const dex_file_;
+ const DexFile::ProtoId* const proto_id_;
+};
+std::ostream& operator<<(std::ostream& os, const Signature& sig);
+
// Iterate and decode class_data_item
class ClassDataItemIterator {
public:
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 272c42d745..543a7b0031 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -56,7 +56,7 @@ static const char kRawDex[] =
"AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";
static const DexFile* OpenDexFileBase64(const char* base64,
- const std::string& location) {
+ const char* location) {
// decode base64
CHECK(base64 != NULL);
size_t length;
@@ -64,7 +64,7 @@ static const DexFile* OpenDexFileBase64(const char* base64,
CHECK(dex_bytes.get() != NULL);
// write to provided file
- UniquePtr<File> file(OS::CreateEmptyFile(location.c_str()));
+ UniquePtr<File> file(OS::CreateEmptyFile(location));
CHECK(file.get() != NULL);
if (!file->WriteFully(dex_bytes.get(), length)) {
PLOG(FATAL) << "Failed to write base64 as dex file";
@@ -73,8 +73,9 @@ static const DexFile* OpenDexFileBase64(const char* base64,
// read dex file
ScopedObjectAccess soa(Thread::Current());
- const DexFile* dex_file = DexFile::Open(location, location);
- CHECK(dex_file != NULL);
+ std::string error_msg;
+ const DexFile* dex_file = DexFile::Open(location, location, &error_msg);
+ CHECK(dex_file != nullptr) << error_msg;
EXPECT_EQ(PROT_READ, dex_file->GetPermissions());
EXPECT_TRUE(dex_file->IsReadOnly());
return dex_file;
@@ -82,7 +83,7 @@ static const DexFile* OpenDexFileBase64(const char* base64,
TEST_F(DexFileTest, Header) {
ScratchFile tmp;
- UniquePtr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename()));
+ UniquePtr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename().c_str()));
ASSERT_TRUE(raw.get() != NULL);
const DexFile::Header& header = raw->GetHeader();
@@ -120,7 +121,9 @@ TEST_F(DexFileTest, GetLocationChecksum) {
TEST_F(DexFileTest, GetChecksum) {
uint32_t checksum;
ScopedObjectAccess soa(Thread::Current());
- EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName(), &checksum));
+ std::string error_msg;
+ EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileName().c_str(), &checksum, &error_msg))
+ << error_msg;
EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
}
@@ -137,14 +140,14 @@ TEST_F(DexFileTest, ClassDefs) {
EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1));
}
-TEST_F(DexFileTest, CreateMethodSignature) {
+TEST_F(DexFileTest, GetMethodSignature) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("CreateMethodSignature"));
+ const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
ASSERT_TRUE(raw != NULL);
EXPECT_EQ(1U, raw->NumClassDefs());
const DexFile::ClassDef& class_def = raw->GetClassDef(0);
- ASSERT_STREQ("LCreateMethodSignature;", raw->GetClassDescriptor(class_def));
+ ASSERT_STREQ("LGetMethodSignature;", raw->GetClassDescriptor(class_def));
const byte* class_data = raw->GetClassData(class_def);
ASSERT_TRUE(class_data != NULL);
@@ -156,11 +159,9 @@ TEST_F(DexFileTest, CreateMethodSignature) {
{
ASSERT_EQ(1U, it.NumDirectMethods());
const DexFile::MethodId& method_id = raw->GetMethodId(it.GetMemberIndex());
- uint32_t proto_idx = method_id.proto_idx_;
const char* name = raw->StringDataByIdx(method_id.name_idx_);
ASSERT_STREQ("<init>", name);
- int32_t length;
- std::string signature(raw->CreateMethodSignature(proto_idx, &length));
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
ASSERT_EQ("()V", signature);
}
@@ -173,9 +174,7 @@ TEST_F(DexFileTest, CreateMethodSignature) {
const char* name = raw->StringDataByIdx(method_id.name_idx_);
ASSERT_STREQ("m1", name);
- uint32_t proto_idx = method_id.proto_idx_;
- int32_t length;
- std::string signature(raw->CreateMethodSignature(proto_idx, &length));
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
ASSERT_EQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", signature);
}
@@ -186,20 +185,18 @@ TEST_F(DexFileTest, CreateMethodSignature) {
const char* name = raw->StringDataByIdx(method_id.name_idx_);
ASSERT_STREQ("m2", name);
- uint32_t proto_idx = method_id.proto_idx_;
- int32_t length;
- std::string signature(raw->CreateMethodSignature(proto_idx, &length));
- ASSERT_EQ("(ZSC)LCreateMethodSignature;", signature);
+ std::string signature(raw->GetMethodSignature(method_id).ToString());
+ ASSERT_EQ("(ZSC)LGetMethodSignature;", signature);
}
}
TEST_F(DexFileTest, FindStringId) {
ScopedObjectAccess soa(Thread::Current());
- const DexFile* raw(OpenTestDexFile("CreateMethodSignature"));
+ const DexFile* raw(OpenTestDexFile("GetMethodSignature"));
ASSERT_TRUE(raw != NULL);
EXPECT_EQ(1U, raw->NumClassDefs());
- const char* strings[] = { "LCreateMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
+ const char* strings[] = { "LGetMethodSignature;", "Ljava/lang/Float;", "Ljava/lang/Object;",
"D", "I", "J", NULL };
for (size_t i = 0; strings[i] != NULL; i++) {
const char* str = strings[i];
@@ -245,11 +242,10 @@ TEST_F(DexFileTest, FindMethodId) {
const DexFile::StringId& name = java_lang_dex_file_->GetStringId(to_find.name_idx_);
const DexFile::ProtoId& signature = java_lang_dex_file_->GetProtoId(to_find.proto_idx_);
const DexFile::MethodId* found = java_lang_dex_file_->FindMethodId(klass, name, signature);
- int32_t length;
ASSERT_TRUE(found != NULL) << "Didn't find method " << i << ": "
<< java_lang_dex_file_->StringByTypeIdx(to_find.class_idx_) << "."
<< java_lang_dex_file_->GetStringData(name)
- << java_lang_dex_file_->CreateMethodSignature(to_find.proto_idx_, &length);
+ << java_lang_dex_file_->GetMethodSignature(to_find);
EXPECT_EQ(java_lang_dex_file_->GetIndexForMethodId(*found), i);
}
}
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 5b076e07b7..56bf21d2cb 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -21,7 +21,7 @@
#include "leb128.h"
#include "safe_map.h"
#include "UniquePtr.h"
-#include "utf.h"
+#include "utf-inl.h"
#include "utils.h"
#include "zip_archive.h"
@@ -65,12 +65,22 @@ static bool IsDataSectionType(uint32_t map_type) {
return true;
}
-static bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
- bool is_return_type) {
+bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size,
+ const char* location, std::string* error_msg) {
+ UniquePtr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location));
+ if (!verifier->Verify()) {
+ *error_msg = verifier->FailureReason();
+ return false;
+ }
+ return true;
+}
+
+bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
+ bool is_return_type) {
switch (shorty_char) {
case 'V':
- if (!is_return_type) {
- LOG(ERROR) << "Invalid use of void";
+ if (UNLIKELY(!is_return_type)) {
+ ErrorStringPrintf("Invalid use of void");
return false;
}
// Intentional fallthrough.
@@ -82,62 +92,58 @@ static bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor,
case 'J':
case 'S':
case 'Z':
- if ((descriptor[0] != shorty_char) || (descriptor[1] != '\0')) {
- LOG(ERROR) << StringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'", shorty_char, descriptor);
+ if (UNLIKELY((descriptor[0] != shorty_char) || (descriptor[1] != '\0'))) {
+ ErrorStringPrintf("Shorty vs. primitive type mismatch: '%c', '%s'",
+ shorty_char, descriptor);
return false;
}
break;
case 'L':
- if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
- LOG(ERROR) << StringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor);
+ if (UNLIKELY((descriptor[0] != 'L') && (descriptor[0] != '['))) {
+ ErrorStringPrintf("Shorty vs. type mismatch: '%c', '%s'", shorty_char, descriptor);
return false;
}
break;
default:
- LOG(ERROR) << "Bad shorty character: '" << shorty_char << "'";
+ ErrorStringPrintf("Bad shorty character: '%c'", shorty_char);
return false;
}
return true;
}
-bool DexFileVerifier::Verify(const DexFile* dex_file, const byte* begin, size_t size) {
- UniquePtr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size));
- return verifier->Verify();
-}
-
-bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) const {
+bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) {
uint32_t range_start = reinterpret_cast<uint32_t>(start);
uint32_t range_end = reinterpret_cast<uint32_t>(end);
uint32_t file_start = reinterpret_cast<uint32_t>(begin_);
uint32_t file_end = file_start + size_;
- if ((range_start < file_start) || (range_start > file_end) ||
- (range_end < file_start) || (range_end > file_end)) {
- LOG(ERROR) << StringPrintf("Bad range for %s: %x to %x", label,
- range_start - file_start, range_end - file_start);
+ if (UNLIKELY((range_start < file_start) || (range_start > file_end) ||
+ (range_end < file_start) || (range_end > file_end))) {
+ ErrorStringPrintf("Bad range for %s: %x to %x", label,
+ range_start - file_start, range_end - file_start);
return false;
}
return true;
}
bool DexFileVerifier::CheckListSize(const void* start, uint32_t count,
- uint32_t element_size, const char* label) const {
+ uint32_t element_size, const char* label) {
const byte* list_start = reinterpret_cast<const byte*>(start);
return CheckPointerRange(list_start, list_start + (count * element_size), label);
}
-bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) const {
- if (field >= limit) {
- LOG(ERROR) << StringPrintf("Bad index for %s: %x >= %x", label, field, limit);
+bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
+ if (UNLIKELY(field >= limit)) {
+ ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
return false;
}
return true;
}
-bool DexFileVerifier::CheckHeader() const {
+bool DexFileVerifier::CheckHeader() {
// Check file size from the header.
uint32_t expected_size = header_->file_size_;
if (size_ != expected_size) {
- LOG(ERROR) << "Bad file size (" << size_ << ", expected " << expected_size << ")";
+ ErrorStringPrintf("Bad file size (%zd, expected %ud)", size_, expected_size);
return false;
}
@@ -147,25 +153,25 @@ bool DexFileVerifier::CheckHeader() const {
const byte* non_sum_ptr = reinterpret_cast<const byte*>(header_) + non_sum;
adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum);
if (adler_checksum != header_->checksum_) {
- LOG(ERROR) << StringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
+ ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_);
return false;
}
// Check the contents of the header.
if (header_->endian_tag_ != DexFile::kDexEndianConstant) {
- LOG(ERROR) << StringPrintf("Unexpected endian_tag: %x", header_->endian_tag_);
+ ErrorStringPrintf("Unexpected endian_tag: %x", header_->endian_tag_);
return false;
}
if (header_->header_size_ != sizeof(DexFile::Header)) {
- LOG(ERROR) << "Bad header size: " << header_->header_size_;
+ ErrorStringPrintf("Bad header size: %ud", header_->header_size_);
return false;
}
return true;
}
-bool DexFileVerifier::CheckMap() const {
+bool DexFileVerifier::CheckMap() {
const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
const DexFile::MapItem* item = map->list_;
@@ -182,19 +188,20 @@ bool DexFileVerifier::CheckMap() const {
// Check the items listed in the map.
for (uint32_t i = 0; i < count; i++) {
- if (last_offset >= item->offset_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out of order map item: %x then %x", last_offset, item->offset_);
+ if (UNLIKELY(last_offset >= item->offset_ && i != 0)) {
+ ErrorStringPrintf("Out of order map item: %x then %x", last_offset, item->offset_);
return false;
}
- if (item->offset_ >= header_->file_size_) {
- LOG(ERROR) << StringPrintf("Map item after end of file: %x, size %x", item->offset_, header_->file_size_);
+ if (UNLIKELY(item->offset_ >= header_->file_size_)) {
+ ErrorStringPrintf("Map item after end of file: %x, size %x",
+ item->offset_, header_->file_size_);
return false;
}
if (IsDataSectionType(item->type_)) {
uint32_t icount = item->size_;
- if (icount > data_items_left) {
- LOG(ERROR) << "Too many items in data section: " << data_item_count + icount;
+ if (UNLIKELY(icount > data_items_left)) {
+ ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount);
return false;
}
data_items_left -= icount;
@@ -203,13 +210,13 @@ bool DexFileVerifier::CheckMap() const {
uint32_t bit = MapTypeToBitMask(item->type_);
- if (bit == 0) {
- LOG(ERROR) << StringPrintf("Unknown map section type %x", item->type_);
+ if (UNLIKELY(bit == 0)) {
+ ErrorStringPrintf("Unknown map section type %x", item->type_);
return false;
}
- if ((used_bits & bit) != 0) {
- LOG(ERROR) << StringPrintf("Duplicate map section of type %x", item->type_);
+ if (UNLIKELY((used_bits & bit) != 0)) {
+ ErrorStringPrintf("Duplicate map section of type %x", item->type_);
return false;
}
@@ -219,63 +226,59 @@ bool DexFileVerifier::CheckMap() const {
}
// Check for missing sections in the map.
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0) {
- LOG(ERROR) << "Map is missing header entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeHeaderItem)) == 0)) {
+ ErrorStringPrintf("Map is missing header entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0) {
- LOG(ERROR) << "Map is missing map_list entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMapList)) == 0)) {
+ ErrorStringPrintf("Map is missing map_list entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 &&
- ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing string_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeStringIdItem)) == 0 &&
+ ((header_->string_ids_off_ != 0) || (header_->string_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing string_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 &&
- ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing type_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeTypeIdItem)) == 0 &&
+ ((header_->type_ids_off_ != 0) || (header_->type_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing type_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 &&
- ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing proto_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeProtoIdItem)) == 0 &&
+ ((header_->proto_ids_off_ != 0) || (header_->proto_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing proto_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 &&
- ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing field_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeFieldIdItem)) == 0 &&
+ ((header_->field_ids_off_ != 0) || (header_->field_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing field_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 &&
- ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0))) {
- LOG(ERROR) << "Map is missing method_ids entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeMethodIdItem)) == 0 &&
+ ((header_->method_ids_off_ != 0) || (header_->method_ids_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing method_ids entry");
return false;
}
- if ((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 &&
- ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0))) {
- LOG(ERROR) << "Map is missing class_defs entry";
+ if (UNLIKELY((used_bits & MapTypeToBitMask(DexFile::kDexTypeClassDefItem)) == 0 &&
+ ((header_->class_defs_off_ != 0) || (header_->class_defs_size_ != 0)))) {
+ ErrorStringPrintf("Map is missing class_defs entry");
return false;
}
-
return true;
}
uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) {
uint32_t result = 0;
- if (!CheckPointerRange(ptr_, ptr_ + size, "encoded_value")) {
- return 0;
- }
-
- for (uint32_t i = 0; i < size; i++) {
- result |= ((uint32_t) *(ptr_++)) << (i * 8);
+ if (LIKELY(CheckPointerRange(ptr_, ptr_ + size, "encoded_value"))) {
+ for (uint32_t i = 0; i < size; i++) {
+ result |= ((uint32_t) *(ptr_++)) << (i * 8);
+ }
}
-
return result;
}
bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
- uint32_t* handler_offsets, uint32_t handlers_size) {
+ uint32_t* handler_offsets, uint32_t handlers_size) {
const byte* handlers_base = DexFile::GetCatchHandlerData(*code_item, 0);
for (uint32_t i = 0; i < handlers_size; i++) {
@@ -283,8 +286,8 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it
uint32_t offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(handlers_base);
int32_t size = DecodeSignedLeb128(&ptr_);
- if ((size < -65536) || (size > 65536)) {
- LOG(ERROR) << "Invalid exception handler size: " << size;
+ if (UNLIKELY((size < -65536) || (size > 65536))) {
+ ErrorStringPrintf("Invalid exception handler size: %d", size);
return false;
}
@@ -304,16 +307,16 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it
}
uint32_t addr = DecodeUnsignedLeb128(&ptr_);
- if (addr >= code_item->insns_size_in_code_units_) {
- LOG(ERROR) << StringPrintf("Invalid handler addr: %x", addr);
+ if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) {
+ ErrorStringPrintf("Invalid handler addr: %x", addr);
return false;
}
}
if (catch_all) {
uint32_t addr = DecodeUnsignedLeb128(&ptr_);
- if (addr >= code_item->insns_size_in_code_units_) {
- LOG(ERROR) << StringPrintf("Invalid handler catch_all_addr: %x", addr);
+ if (UNLIKELY(addr >= code_item->insns_size_in_code_units_)) {
+ ErrorStringPrintf("Invalid handler catch_all_addr: %x", addr);
return false;
}
}
@@ -323,21 +326,21 @@ bool DexFileVerifier::CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_it
}
bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flags,
- bool expect_static) const {
+ bool expect_static) {
if (!CheckIndex(idx, header_->field_ids_size_, "class_data_item field_idx")) {
return false;
}
bool is_static = (access_flags & kAccStatic) != 0;
- if (is_static != expect_static) {
- LOG(ERROR) << "Static/instance field not in expected list";
+ if (UNLIKELY(is_static != expect_static)) {
+ ErrorStringPrintf("Static/instance field not in expected list");
return false;
}
uint32_t access_field_mask = kAccPublic | kAccPrivate | kAccProtected | kAccStatic |
kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum;
- if ((access_flags & ~access_field_mask) != 0) {
- LOG(ERROR) << StringPrintf("Bad class_data_item field access_flags %x", access_flags);
+ if (UNLIKELY((access_flags & ~access_field_mask) != 0)) {
+ ErrorStringPrintf("Bad class_data_item field access_flags %x", access_flags);
return false;
}
@@ -345,7 +348,7 @@ bool DexFileVerifier::CheckClassDataItemField(uint32_t idx, uint32_t access_flag
}
bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags,
- uint32_t code_offset, bool expect_direct) const {
+ uint32_t code_offset, bool expect_direct) {
if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
return false;
}
@@ -355,26 +358,27 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_fla
bool is_synchronized = (access_flags & kAccSynchronized) != 0;
bool allow_synchronized = (access_flags & kAccNative) != 0;
- if (is_direct != expect_direct) {
- LOG(ERROR) << "Direct/virtual method not in expected list";
+ if (UNLIKELY(is_direct != expect_direct)) {
+ ErrorStringPrintf("Direct/virtual method not in expected list");
return false;
}
uint32_t access_method_mask = kAccPublic | kAccPrivate | kAccProtected | kAccStatic |
kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | kAccAbstract |
kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized;
- if (((access_flags & ~access_method_mask) != 0) || (is_synchronized && !allow_synchronized)) {
- LOG(ERROR) << StringPrintf("Bad class_data_item method access_flags %x", access_flags);
+ if (UNLIKELY(((access_flags & ~access_method_mask) != 0) ||
+ (is_synchronized && !allow_synchronized))) {
+ ErrorStringPrintf("Bad class_data_item method access_flags %x", access_flags);
return false;
}
- if (expect_code && code_offset == 0) {
- LOG(ERROR)<< StringPrintf("Unexpected zero value for class_data_item method code_off"
- " with access flags %x", access_flags);
+ if (UNLIKELY(expect_code && (code_offset == 0))) {
+ ErrorStringPrintf("Unexpected zero value for class_data_item method code_off with access "
+ "flags %x", access_flags);
return false;
- } else if (!expect_code && code_offset != 0) {
- LOG(ERROR) << StringPrintf("Unexpected non-zero value %x for class_data_item method code_off"
- " with access flags %x", code_offset, access_flags);
+ } else if (UNLIKELY(!expect_code && (code_offset != 0))) {
+ ErrorStringPrintf("Unexpected non-zero value %x for class_data_item method code_off"
+ " with access flags %x", code_offset, access_flags);
return false;
}
@@ -387,8 +391,8 @@ bool DexFileVerifier::CheckPadding(uint32_t offset, uint32_t aligned_offset) {
return false;
}
while (offset < aligned_offset) {
- if (*ptr_ != '\0') {
- LOG(ERROR) << StringPrintf("Non-zero padding %x before section start at %x", *ptr_, offset);
+ if (UNLIKELY(*ptr_ != '\0')) {
+ ErrorStringPrintf("Non-zero padding %x before section start at %x", *ptr_, offset);
return false;
}
ptr_++;
@@ -409,24 +413,24 @@ bool DexFileVerifier::CheckEncodedValue() {
switch (value_type) {
case DexFile::kDexAnnotationByte:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value byte size %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value byte size %x", value_arg);
return false;
}
ptr_++;
break;
case DexFile::kDexAnnotationShort:
case DexFile::kDexAnnotationChar:
- if (value_arg > 1) {
- LOG(ERROR) << StringPrintf("Bad encoded_value char/short size %x", value_arg);
+ if (UNLIKELY(value_arg > 1)) {
+ ErrorStringPrintf("Bad encoded_value char/short size %x", value_arg);
return false;
}
ptr_ += value_arg + 1;
break;
case DexFile::kDexAnnotationInt:
case DexFile::kDexAnnotationFloat:
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value int/float size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value int/float size %x", value_arg);
return false;
}
ptr_ += value_arg + 1;
@@ -436,8 +440,8 @@ bool DexFileVerifier::CheckEncodedValue() {
ptr_ += value_arg + 1;
break;
case DexFile::kDexAnnotationString: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value string size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value string size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -447,8 +451,8 @@ bool DexFileVerifier::CheckEncodedValue() {
break;
}
case DexFile::kDexAnnotationType: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value type size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value type size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -459,8 +463,8 @@ bool DexFileVerifier::CheckEncodedValue() {
}
case DexFile::kDexAnnotationField:
case DexFile::kDexAnnotationEnum: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value field/enum size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value field/enum size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -470,8 +474,8 @@ bool DexFileVerifier::CheckEncodedValue() {
break;
}
case DexFile::kDexAnnotationMethod: {
- if (value_arg > 3) {
- LOG(ERROR) << StringPrintf("Bad encoded_value method size %x", value_arg);
+ if (UNLIKELY(value_arg > 3)) {
+ ErrorStringPrintf("Bad encoded_value method size %x", value_arg);
return false;
}
uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1);
@@ -481,8 +485,8 @@ bool DexFileVerifier::CheckEncodedValue() {
break;
}
case DexFile::kDexAnnotationArray:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value array value_arg %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value array value_arg %x", value_arg);
return false;
}
if (!CheckEncodedArray()) {
@@ -490,8 +494,8 @@ bool DexFileVerifier::CheckEncodedValue() {
}
break;
case DexFile::kDexAnnotationAnnotation:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value annotation value_arg %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value annotation value_arg %x", value_arg);
return false;
}
if (!CheckEncodedAnnotation()) {
@@ -499,19 +503,19 @@ bool DexFileVerifier::CheckEncodedValue() {
}
break;
case DexFile::kDexAnnotationNull:
- if (value_arg != 0) {
- LOG(ERROR) << StringPrintf("Bad encoded_value null value_arg %x", value_arg);
+ if (UNLIKELY(value_arg != 0)) {
+ ErrorStringPrintf("Bad encoded_value null value_arg %x", value_arg);
return false;
}
break;
case DexFile::kDexAnnotationBoolean:
- if (value_arg > 1) {
- LOG(ERROR) << StringPrintf("Bad encoded_value boolean size %x", value_arg);
+ if (UNLIKELY(value_arg > 1)) {
+ ErrorStringPrintf("Bad encoded_value boolean size %x", value_arg);
return false;
}
break;
default:
- LOG(ERROR) << StringPrintf("Bogus encoded_value value_type %x", value_type);
+ ErrorStringPrintf("Bogus encoded_value value_type %x", value_type);
return false;
}
@@ -523,7 +527,7 @@ bool DexFileVerifier::CheckEncodedArray() {
while (size--) {
if (!CheckEncodedValue()) {
- LOG(ERROR) << "Bad encoded_array value";
+ failure_reason_ = StringPrintf("Bad encoded_array value: %s", failure_reason_.c_str());
return false;
}
}
@@ -545,9 +549,9 @@ bool DexFileVerifier::CheckEncodedAnnotation() {
return false;
}
- if (last_idx >= idx && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order annotation_element name_idx: %x then %x",
- last_idx, idx);
+ if (UNLIKELY(last_idx >= idx && i != 0)) {
+ ErrorStringPrintf("Out-of-order annotation_element name_idx: %x then %x",
+ last_idx, idx);
return false;
}
@@ -596,21 +600,22 @@ bool DexFileVerifier::CheckIntraCodeItem() {
return false;
}
- if (code_item->ins_size_ > code_item->registers_size_) {
- LOG(ERROR) << "ins_size (" << code_item->ins_size_ << ") > registers_size ("
- << code_item->registers_size_ << ")";
+ if (UNLIKELY(code_item->ins_size_ > code_item->registers_size_)) {
+ ErrorStringPrintf("ins_size (%ud) > registers_size (%ud)",
+ code_item->ins_size_, code_item->registers_size_);
return false;
}
- if ((code_item->outs_size_ > 5) && (code_item->outs_size_ > code_item->registers_size_)) {
+ if (UNLIKELY((code_item->outs_size_ > 5) &&
+ (code_item->outs_size_ > code_item->registers_size_))) {
/*
* outs_size can be up to 5, even if registers_size is smaller, since the
* short forms of method invocation allow repetitions of a register multiple
* times within a single parameter list. However, longer parameter lists
* need to be represented in-order in the register file.
*/
- LOG(ERROR) << "outs_size (" << code_item->outs_size_ << ") > registers_size ("
- << code_item->registers_size_ << ")";
+ ErrorStringPrintf("outs_size (%ud) > registers_size (%ud)",
+ code_item->outs_size_, code_item->registers_size_);
return false;
}
@@ -629,7 +634,7 @@ bool DexFileVerifier::CheckIntraCodeItem() {
// try_items are 4-byte aligned. Verify the spacer is 0.
if ((((uint32_t) &insns[insns_size] & 3) != 0) && (insns[insns_size] != 0)) {
- LOG(ERROR) << StringPrintf("Non-zero padding: %x", insns[insns_size]);
+ ErrorStringPrintf("Non-zero padding: %x", insns[insns_size]);
return false;
}
@@ -641,8 +646,8 @@ bool DexFileVerifier::CheckIntraCodeItem() {
return false;
}
- if ((handlers_size == 0) || (handlers_size >= 65536)) {
- LOG(ERROR) << "Invalid handlers_size: " << handlers_size;
+ if (UNLIKELY((handlers_size == 0) || (handlers_size >= 65536))) {
+ ErrorStringPrintf("Invalid handlers_size: %ud", handlers_size);
return false;
}
@@ -653,14 +658,13 @@ bool DexFileVerifier::CheckIntraCodeItem() {
uint32_t last_addr = 0;
while (try_items_size--) {
- if (try_items->start_addr_ < last_addr) {
- LOG(ERROR) << StringPrintf("Out-of_order try_item with start_addr: %x",
- try_items->start_addr_);
+ if (UNLIKELY(try_items->start_addr_ < last_addr)) {
+ ErrorStringPrintf("Out-of_order try_item with start_addr: %x", try_items->start_addr_);
return false;
}
- if (try_items->start_addr_ >= insns_size) {
- LOG(ERROR) << StringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_);
+ if (UNLIKELY(try_items->start_addr_ >= insns_size)) {
+ ErrorStringPrintf("Invalid try_item start_addr: %x", try_items->start_addr_);
return false;
}
@@ -671,14 +675,14 @@ bool DexFileVerifier::CheckIntraCodeItem() {
}
}
- if (i == handlers_size) {
- LOG(ERROR) << StringPrintf("Bogus handler offset: %x", try_items->handler_off_);
+ if (UNLIKELY(i == handlers_size)) {
+ ErrorStringPrintf("Bogus handler offset: %x", try_items->handler_off_);
return false;
}
last_addr = try_items->start_addr_ + try_items->insn_count_;
- if (last_addr > insns_size) {
- LOG(ERROR) << StringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_);
+ if (UNLIKELY(last_addr > insns_size)) {
+ ErrorStringPrintf("Invalid try_item insn_count: %x", try_items->insn_count_);
return false;
}
@@ -693,8 +697,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() {
const byte* file_end = begin_ + size_;
for (uint32_t i = 0; i < size; i++) {
- if (ptr_ >= file_end) {
- LOG(ERROR) << "String data would go beyond end-of-file";
+ if (UNLIKELY(ptr_ >= file_end)) {
+ ErrorStringPrintf("String data would go beyond end-of-file");
return false;
}
@@ -704,8 +708,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() {
switch (byte >> 4) {
case 0x00:
// Special case of bit pattern 0xxx.
- if (byte == 0) {
- LOG(ERROR) << StringPrintf("String data shorter than indicated utf16_size %x", size);
+ if (UNLIKELY(byte == 0)) {
+ ErrorStringPrintf("String data shorter than indicated utf16_size %x", size);
return false;
}
break;
@@ -725,19 +729,19 @@ bool DexFileVerifier::CheckIntraStringDataItem() {
case 0x0f:
// Illegal bit patterns 10xx or 1111.
// Note: 1111 is valid for normal UTF-8, but not here.
- LOG(ERROR) << StringPrintf("Illegal start byte %x in string data", byte);
+ ErrorStringPrintf("Illegal start byte %x in string data", byte);
return false;
case 0x0c:
case 0x0d: {
// Bit pattern 110x has an additional byte.
uint8_t byte2 = *(ptr_++);
- if ((byte2 & 0xc0) != 0x80) {
- LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte2);
+ if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
return false;
}
uint16_t value = ((byte & 0x1f) << 6) | (byte2 & 0x3f);
- if ((value != 0) && (value < 0x80)) {
- LOG(ERROR) << StringPrintf("Illegal representation for value %x in string data", value);
+ if (UNLIKELY((value != 0) && (value < 0x80))) {
+ ErrorStringPrintf("Illegal representation for value %x in string data", value);
return false;
}
break;
@@ -745,18 +749,18 @@ bool DexFileVerifier::CheckIntraStringDataItem() {
case 0x0e: {
// Bit pattern 1110 has 2 additional bytes.
uint8_t byte2 = *(ptr_++);
- if ((byte2 & 0xc0) != 0x80) {
- LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte2);
+ if (UNLIKELY((byte2 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte2);
return false;
}
uint8_t byte3 = *(ptr_++);
- if ((byte3 & 0xc0) != 0x80) {
- LOG(ERROR) << StringPrintf("Illegal continuation byte %x in string data", byte3);
+ if (UNLIKELY((byte3 & 0xc0) != 0x80)) {
+ ErrorStringPrintf("Illegal continuation byte %x in string data", byte3);
return false;
}
uint16_t value = ((byte & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f);
- if (value < 0x800) {
- LOG(ERROR) << StringPrintf("Illegal representation for value %x in string data", value);
+ if (UNLIKELY(value < 0x800)) {
+ ErrorStringPrintf("Illegal representation for value %x in string data", value);
return false;
}
break;
@@ -764,8 +768,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() {
}
}
- if (*(ptr_++) != '\0') {
- LOG(ERROR) << StringPrintf("String longer than indicated size %x", size);
+ if (UNLIKELY(*(ptr_++) != '\0')) {
+ ErrorStringPrintf("String longer than indicated size %x", size);
return false;
}
@@ -775,8 +779,8 @@ bool DexFileVerifier::CheckIntraStringDataItem() {
bool DexFileVerifier::CheckIntraDebugInfoItem() {
DecodeUnsignedLeb128(&ptr_);
uint32_t parameters_size = DecodeUnsignedLeb128(&ptr_);
- if (parameters_size > 65536) {
- LOG(ERROR) << StringPrintf("Invalid parameters_size: %x", parameters_size);
+ if (UNLIKELY(parameters_size > 65536)) {
+ ErrorStringPrintf("Invalid parameters_size: %x", parameters_size);
return false;
}
@@ -806,8 +810,8 @@ bool DexFileVerifier::CheckIntraDebugInfoItem() {
}
case DexFile::DBG_START_LOCAL: {
uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
- if (reg_num >= 65536) {
- LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
@@ -829,16 +833,16 @@ bool DexFileVerifier::CheckIntraDebugInfoItem() {
case DexFile::DBG_END_LOCAL:
case DexFile::DBG_RESTART_LOCAL: {
uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
- if (reg_num >= 65536) {
- LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
break;
}
case DexFile::DBG_START_LOCAL_EXTENDED: {
uint32_t reg_num = DecodeUnsignedLeb128(&ptr_);
- if (reg_num >= 65536) {
- LOG(ERROR) << StringPrintf("Bad reg_num for opcode %x", opcode);
+ if (UNLIKELY(reg_num >= 65536)) {
+ ErrorStringPrintf("Bad reg_num for opcode %x", opcode);
return false;
}
uint32_t name_idx = DecodeUnsignedLeb128(&ptr_);
@@ -890,7 +894,7 @@ bool DexFileVerifier::CheckIntraAnnotationItem() {
case DexFile::kDexVisibilitySystem:
break;
default:
- LOG(ERROR) << StringPrintf("Bad annotation visibility: %x", *ptr_);
+ ErrorStringPrintf("Bad annotation visibility: %x", *ptr_);
return false;
}
@@ -918,8 +922,8 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
uint32_t last_idx = 0;
for (uint32_t i = 0; i < field_count; i++) {
- if (last_idx >= field_item->field_idx_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_);
+ if (UNLIKELY(last_idx >= field_item->field_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order field_idx for annotation: %x then %x", last_idx, field_item->field_idx_);
return false;
}
last_idx = field_item->field_idx_;
@@ -936,9 +940,9 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
last_idx = 0;
for (uint32_t i = 0; i < method_count; i++) {
- if (last_idx >= method_item->method_idx_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x",
- last_idx, method_item->method_idx_);
+ if (UNLIKELY(last_idx >= method_item->method_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
+ last_idx, method_item->method_idx_);
return false;
}
last_idx = method_item->method_idx_;
@@ -950,15 +954,15 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
reinterpret_cast<const DexFile::ParameterAnnotationsItem*>(method_item);
uint32_t parameter_count = item->parameters_size_;
if (!CheckListSize(parameter_item, parameter_count, sizeof(DexFile::ParameterAnnotationsItem),
- "parameter_annotations list")) {
+ "parameter_annotations list")) {
return false;
}
last_idx = 0;
for (uint32_t i = 0; i < parameter_count; i++) {
- if (last_idx >= parameter_item->method_idx_ && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order method_idx for annotation: %x then %x",
- last_idx, parameter_item->method_idx_);
+ if (UNLIKELY(last_idx >= parameter_item->method_idx_ && i != 0)) {
+ ErrorStringPrintf("Out-of-order method_idx for annotation: %x then %x",
+ last_idx, parameter_item->method_idx_);
return false;
}
last_idx = parameter_item->method_idx_;
@@ -1059,7 +1063,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count,
if (!CheckPointerRange(list, list + 1, "annotation_set_ref_list") ||
!CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem),
- "annotation_set_ref_list size")) {
+ "annotation_set_ref_list size")) {
return false;
}
ptr_ = reinterpret_cast<const byte*>(item + count);
@@ -1121,7 +1125,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count,
break;
}
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1130,8 +1134,8 @@ bool DexFileVerifier::CheckIntraSectionIterate(uint32_t offset, uint32_t count,
}
aligned_offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
- if (aligned_offset > size_) {
- LOG(ERROR) << StringPrintf("Item %d at ends out of bounds", i);
+ if (UNLIKELY(aligned_offset > size_)) {
+ ErrorStringPrintf("Item %d at ends out of bounds", i);
return false;
}
@@ -1172,17 +1176,17 @@ bool DexFileVerifier::CheckIntraIdSection(uint32_t offset, uint32_t count, uint1
expected_size = header_->class_defs_size_;
break;
default:
- LOG(ERROR) << StringPrintf("Bad type for id section: %x", type);
+ ErrorStringPrintf("Bad type for id section: %x", type);
return false;
}
// Check that the offset and size are what were expected from the header.
- if (offset != expected_offset) {
- LOG(ERROR) << StringPrintf("Bad offset for section: got %x, expected %x", offset, expected_offset);
+ if (UNLIKELY(offset != expected_offset)) {
+ ErrorStringPrintf("Bad offset for section: got %x, expected %x", offset, expected_offset);
return false;
}
- if (count != expected_size) {
- LOG(ERROR) << StringPrintf("Bad size for section: got %x, expected %x", count, expected_size);
+ if (UNLIKELY(count != expected_size)) {
+ ErrorStringPrintf("Bad size for section: got %x, expected %x", count, expected_size);
return false;
}
@@ -1194,8 +1198,8 @@ bool DexFileVerifier::CheckIntraDataSection(uint32_t offset, uint32_t count, uin
uint32_t data_end = data_start + header_->data_size_;
// Sanity check the offset of the section.
- if ((offset < data_start) || (offset > data_end)) {
- LOG(ERROR) << StringPrintf("Bad offset for data subsection: %x", offset);
+ if (UNLIKELY((offset < data_start) || (offset > data_end))) {
+ ErrorStringPrintf("Bad offset for data subsection: %x", offset);
return false;
}
@@ -1205,7 +1209,7 @@ bool DexFileVerifier::CheckIntraDataSection(uint32_t offset, uint32_t count, uin
uint32_t next_offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
if (next_offset > data_end) {
- LOG(ERROR) << StringPrintf("Out-of-bounds end of data subsection: %x", next_offset);
+ ErrorStringPrintf("Out-of-bounds end of data subsection: %x", next_offset);
return false;
}
@@ -1229,20 +1233,20 @@ bool DexFileVerifier::CheckIntraSection() {
// Check for padding and overlap between items.
if (!CheckPadding(offset, section_offset)) {
return false;
- } else if (offset > section_offset) {
- LOG(ERROR) << StringPrintf("Section overlap or out-of-order map: %x, %x", offset, section_offset);
+ } else if (UNLIKELY(offset > section_offset)) {
+ ErrorStringPrintf("Section overlap or out-of-order map: %x, %x", offset, section_offset);
return false;
}
// Check each item based on its type.
switch (type) {
case DexFile::kDexTypeHeaderItem:
- if (section_count != 1) {
- LOG(ERROR) << "Multiple header items";
+ if (UNLIKELY(section_count != 1)) {
+ ErrorStringPrintf("Multiple header items");
return false;
}
- if (section_offset != 0) {
- LOG(ERROR) << StringPrintf("Header at %x, not at start of file", section_offset);
+ if (UNLIKELY(section_offset != 0)) {
+ ErrorStringPrintf("Header at %x, not at start of file", section_offset);
return false;
}
ptr_ = begin_ + header_->header_size_;
@@ -1260,13 +1264,13 @@ bool DexFileVerifier::CheckIntraSection() {
offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
break;
case DexFile::kDexTypeMapList:
- if (section_count != 1) {
- LOG(ERROR) << "Multiple map list items";
+ if (UNLIKELY(section_count != 1)) {
+ ErrorStringPrintf("Multiple map list items");
return false;
}
- if (section_offset != header_->map_off_) {
- LOG(ERROR) << StringPrintf("Map not at header-defined offset: %x, expected %x",
- section_offset, header_->map_off_);
+ if (UNLIKELY(section_offset != header_->map_off_)) {
+ ErrorStringPrintf("Map not at header-defined offset: %x, expected %x",
+ section_offset, header_->map_off_);
return false;
}
ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem));
@@ -1288,7 +1292,7 @@ bool DexFileVerifier::CheckIntraSection() {
offset = reinterpret_cast<uint32_t>(ptr_) - reinterpret_cast<uint32_t>(begin_);
break;
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1300,13 +1304,13 @@ bool DexFileVerifier::CheckIntraSection() {
bool DexFileVerifier::CheckOffsetToTypeMap(uint32_t offset, uint16_t type) {
auto it = offset_to_type_map_.find(offset);
- if (it == offset_to_type_map_.end()) {
- LOG(ERROR) << StringPrintf("No data map entry found @ %x; expected %x", offset, type);
+ if (UNLIKELY(it == offset_to_type_map_.end())) {
+ ErrorStringPrintf("No data map entry found @ %x; expected %x", offset, type);
return false;
}
- if (it->second != type) {
- LOG(ERROR) << StringPrintf("Unexpected data map entry @ %x; expected %x, found %x",
- offset, type, it->second);
+ if (UNLIKELY(it->second != type)) {
+ ErrorStringPrintf("Unexpected data map entry @ %x; expected %x, found %x",
+ offset, type, it->second);
return false;
}
return true;
@@ -1365,8 +1369,8 @@ bool DexFileVerifier::CheckInterStringIdItem() {
const DexFile::StringId* prev_item = reinterpret_cast<const DexFile::StringId*>(previous_item_);
const char* prev_str = dex_file_->GetStringData(*prev_item);
const char* str = dex_file_->GetStringData(*item);
- if (CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0) {
- LOG(ERROR) << StringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str);
+ if (UNLIKELY(CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(prev_str, str) >= 0)) {
+ ErrorStringPrintf("Out-of-order string_ids: '%s' then '%s'", prev_str, str);
return false;
}
}
@@ -1380,17 +1384,17 @@ bool DexFileVerifier::CheckInterTypeIdItem() {
const char* descriptor = dex_file_->StringDataByIdx(item->descriptor_idx_);
// Check that the descriptor is a valid type.
- if (!IsValidDescriptor(descriptor)) {
- LOG(ERROR) << StringPrintf("Invalid type descriptor: '%s'", descriptor);
+ if (UNLIKELY(!IsValidDescriptor(descriptor))) {
+ ErrorStringPrintf("Invalid type descriptor: '%s'", descriptor);
return false;
}
// Check ordering between items.
if (previous_item_ != NULL) {
const DexFile::TypeId* prev_item = reinterpret_cast<const DexFile::TypeId*>(previous_item_);
- if (prev_item->descriptor_idx_ >= item->descriptor_idx_) {
- LOG(ERROR) << StringPrintf("Out-of-order type_ids: %x then %x",
- prev_item->descriptor_idx_, item->descriptor_idx_);
+ if (UNLIKELY(prev_item->descriptor_idx_ >= item->descriptor_idx_)) {
+ ErrorStringPrintf("Out-of-order type_ids: %x then %x",
+ prev_item->descriptor_idx_, item->descriptor_idx_);
return false;
}
}
@@ -1422,16 +1426,16 @@ bool DexFileVerifier::CheckInterProtoIdItem() {
it.Next();
shorty++;
}
- if (it.HasNext() || *shorty != '\0') {
- LOG(ERROR) << "Mismatched length for parameters and shorty";
+ if (UNLIKELY(it.HasNext() || *shorty != '\0')) {
+ ErrorStringPrintf("Mismatched length for parameters and shorty");
return false;
}
// Check ordering between items. This relies on type_ids being in order.
if (previous_item_ != NULL) {
const DexFile::ProtoId* prev = reinterpret_cast<const DexFile::ProtoId*>(previous_item_);
- if (prev->return_type_idx_ > item->return_type_idx_) {
- LOG(ERROR) << "Out-of-order proto_id return types";
+ if (UNLIKELY(prev->return_type_idx_ > item->return_type_idx_)) {
+ ErrorStringPrintf("Out-of-order proto_id return types");
return false;
} else if (prev->return_type_idx_ == item->return_type_idx_) {
DexFileParameterIterator curr_it(*dex_file_, *item);
@@ -1443,15 +1447,15 @@ bool DexFileVerifier::CheckInterProtoIdItem() {
if (prev_idx == DexFile::kDexNoIndex16) {
break;
}
- if (curr_idx == DexFile::kDexNoIndex16) {
- LOG(ERROR) << "Out-of-order proto_id arguments";
+ if (UNLIKELY(curr_idx == DexFile::kDexNoIndex16)) {
+ ErrorStringPrintf("Out-of-order proto_id arguments");
return false;
}
if (prev_idx < curr_idx) {
break;
- } else if (prev_idx > curr_idx) {
- LOG(ERROR) << "Out-of-order proto_id arguments";
+ } else if (UNLIKELY(prev_idx > curr_idx)) {
+ ErrorStringPrintf("Out-of-order proto_id arguments");
return false;
}
@@ -1470,38 +1474,38 @@ bool DexFileVerifier::CheckInterFieldIdItem() {
// Check that the class descriptor is valid.
const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid descriptor for class_idx: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor);
return false;
}
// Check that the type descriptor is a valid field name.
descriptor = dex_file_->StringByTypeIdx(item->type_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] == 'V') {
- LOG(ERROR) << "Invalid descriptor for type_idx: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] == 'V')) {
+ ErrorStringPrintf("Invalid descriptor for type_idx: '%s'", descriptor);
return false;
}
// Check that the name is valid.
descriptor = dex_file_->StringDataByIdx(item->name_idx_);
- if (!IsValidMemberName(descriptor)) {
- LOG(ERROR) << "Invalid field name: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidMemberName(descriptor))) {
+ ErrorStringPrintf("Invalid field name: '%s'", descriptor);
return false;
}
// Check ordering between items. This relies on the other sections being in order.
if (previous_item_ != NULL) {
const DexFile::FieldId* prev_item = reinterpret_cast<const DexFile::FieldId*>(previous_item_);
- if (prev_item->class_idx_ > item->class_idx_) {
- LOG(ERROR) << "Out-of-order field_ids";
+ if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
return false;
} else if (prev_item->class_idx_ == item->class_idx_) {
- if (prev_item->name_idx_ > item->name_idx_) {
- LOG(ERROR) << "Out-of-order field_ids";
+ if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
return false;
} else if (prev_item->name_idx_ == item->name_idx_) {
- if (prev_item->type_idx_ >= item->type_idx_) {
- LOG(ERROR) << "Out-of-order field_ids";
+ if (UNLIKELY(prev_item->type_idx_ >= item->type_idx_)) {
+ ErrorStringPrintf("Out-of-order field_ids");
return false;
}
}
@@ -1517,31 +1521,31 @@ bool DexFileVerifier::CheckInterMethodIdItem() {
// Check that the class descriptor is a valid reference name.
const char* descriptor = dex_file_->StringByTypeIdx(item->class_idx_);
- if (!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '[')) {
- LOG(ERROR) << "Invalid descriptor for class_idx: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || (descriptor[0] != 'L' && descriptor[0] != '['))) {
+ ErrorStringPrintf("Invalid descriptor for class_idx: '%s'", descriptor);
return false;
}
// Check that the name is valid.
descriptor = dex_file_->StringDataByIdx(item->name_idx_);
- if (!IsValidMemberName(descriptor)) {
- LOG(ERROR) << "Invalid method name: '" << descriptor << '"';
+ if (UNLIKELY(!IsValidMemberName(descriptor))) {
+ ErrorStringPrintf("Invalid method name: '%s'", descriptor);
return false;
}
// Check ordering between items. This relies on the other sections being in order.
if (previous_item_ != NULL) {
const DexFile::MethodId* prev_item = reinterpret_cast<const DexFile::MethodId*>(previous_item_);
- if (prev_item->class_idx_ > item->class_idx_) {
- LOG(ERROR) << "Out-of-order method_ids";
+ if (UNLIKELY(prev_item->class_idx_ > item->class_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
return false;
} else if (prev_item->class_idx_ == item->class_idx_) {
- if (prev_item->name_idx_ > item->name_idx_) {
- LOG(ERROR) << "Out-of-order method_ids";
+ if (UNLIKELY(prev_item->name_idx_ > item->name_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
return false;
} else if (prev_item->name_idx_ == item->name_idx_) {
- if (prev_item->proto_idx_ >= item->proto_idx_) {
- LOG(ERROR) << "Out-of-order method_ids";
+ if (UNLIKELY(prev_item->proto_idx_ >= item->proto_idx_)) {
+ ErrorStringPrintf("Out-of-order method_ids");
return false;
}
}
@@ -1557,8 +1561,8 @@ bool DexFileVerifier::CheckInterClassDefItem() {
uint32_t class_idx = item->class_idx_;
const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid class descriptor: '" << descriptor << "'";
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid class descriptor: '%s'", descriptor);
return false;
}
@@ -1581,8 +1585,8 @@ bool DexFileVerifier::CheckInterClassDefItem() {
if (item->superclass_idx_ != DexFile::kDexNoIndex16) {
descriptor = dex_file_->StringByTypeIdx(item->superclass_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid superclass: '" << descriptor << "'";
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid superclass: '%s'", descriptor);
return false;
}
}
@@ -1594,8 +1598,8 @@ bool DexFileVerifier::CheckInterClassDefItem() {
// Ensure that all interfaces refer to classes (not arrays or primitives).
for (uint32_t i = 0; i < size; i++) {
descriptor = dex_file_->StringByTypeIdx(interfaces->GetTypeItem(i).type_idx_);
- if (!IsValidDescriptor(descriptor) || descriptor[0] != 'L') {
- LOG(ERROR) << "Invalid interface: '" << descriptor << "'";
+ if (UNLIKELY(!IsValidDescriptor(descriptor) || descriptor[0] != 'L')) {
+ ErrorStringPrintf("Invalid interface: '%s'", descriptor);
return false;
}
}
@@ -1608,8 +1612,8 @@ bool DexFileVerifier::CheckInterClassDefItem() {
uint32_t idx1 = interfaces->GetTypeItem(i).type_idx_;
for (uint32_t j =0; j < i; j++) {
uint32_t idx2 = interfaces->GetTypeItem(j).type_idx_;
- if (idx1 == idx2) {
- LOG(ERROR) << "Duplicate interface: '" << dex_file_->StringByTypeIdx(idx1) << "'";
+ if (UNLIKELY(idx1 == idx2)) {
+ ErrorStringPrintf("Duplicate interface: '%s'", dex_file_->StringByTypeIdx(idx1));
return false;
}
}
@@ -1620,8 +1624,8 @@ bool DexFileVerifier::CheckInterClassDefItem() {
if (item->class_data_off_ != 0) {
const byte* data = begin_ + item->class_data_off_;
uint16_t data_definer = FindFirstClassDataDefiner(data);
- if ((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16)) {
- LOG(ERROR) << "Invalid class_data_item";
+ if (UNLIKELY((data_definer != item->class_idx_) && (data_definer != DexFile::kDexNoIndex16))) {
+ ErrorStringPrintf("Invalid class_data_item");
return false;
}
}
@@ -1630,8 +1634,9 @@ bool DexFileVerifier::CheckInterClassDefItem() {
if (item->annotations_off_ != 0) {
const byte* data = begin_ + item->annotations_off_;
uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data);
- if ((annotations_definer != item->class_idx_) && (annotations_definer != DexFile::kDexNoIndex16)) {
- LOG(ERROR) << "Invalid annotations_directory_item";
+ if (UNLIKELY((annotations_definer != item->class_idx_) &&
+ (annotations_definer != DexFile::kDexNoIndex16))) {
+ ErrorStringPrintf("Invalid annotations_directory_item");
return false;
}
}
@@ -1675,8 +1680,8 @@ bool DexFileVerifier::CheckInterAnnotationSetItem() {
const uint8_t* data = annotation->annotation_;
uint32_t idx = DecodeUnsignedLeb128(&data);
- if (last_idx >= idx && i != 0) {
- LOG(ERROR) << StringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
+ if (UNLIKELY(last_idx >= idx && i != 0)) {
+ ErrorStringPrintf("Out-of-order entry types: %x then %x", last_idx, idx);
return false;
}
@@ -1694,8 +1699,8 @@ bool DexFileVerifier::CheckInterClassDataItem() {
for (; it.HasNextStaticField() || it.HasNextInstanceField(); it.Next()) {
const DexFile::FieldId& field = dex_file_->GetFieldId(it.GetMemberIndex());
- if (field.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for class_data_item field";
+ if (UNLIKELY(field.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for class_data_item field");
return false;
}
}
@@ -1705,8 +1710,8 @@ bool DexFileVerifier::CheckInterClassDataItem() {
return false;
}
const DexFile::MethodId& method = dex_file_->GetMethodId(it.GetMemberIndex());
- if (method.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for class_data_item method";
+ if (UNLIKELY(method.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for class_data_item method");
return false;
}
}
@@ -1731,8 +1736,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
uint32_t field_count = item->fields_size_;
for (uint32_t i = 0; i < field_count; i++) {
const DexFile::FieldId& field = dex_file_->GetFieldId(field_item->field_idx_);
- if (field.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for field_annotation";
+ if (UNLIKELY(field.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for field_annotation");
return false;
}
if (!CheckOffsetToTypeMap(field_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
@@ -1747,8 +1752,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
uint32_t method_count = item->methods_size_;
for (uint32_t i = 0; i < method_count; i++) {
const DexFile::MethodId& method = dex_file_->GetMethodId(method_item->method_idx_);
- if (method.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for method_annotation";
+ if (UNLIKELY(method.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for method_annotation");
return false;
}
if (!CheckOffsetToTypeMap(method_item->annotations_off_, DexFile::kDexTypeAnnotationSetItem)) {
@@ -1763,8 +1768,8 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() {
uint32_t parameter_count = item->parameters_size_;
for (uint32_t i = 0; i < parameter_count; i++) {
const DexFile::MethodId& parameter_method = dex_file_->GetMethodId(parameter_item->method_idx_);
- if (parameter_method.class_idx_ != defining_class) {
- LOG(ERROR) << "Mismatched defining class for parameter_annotation";
+ if (UNLIKELY(parameter_method.class_idx_ != defining_class)) {
+ ErrorStringPrintf("Mismatched defining class for parameter_annotation");
return false;
}
if (!CheckOffsetToTypeMap(parameter_item->annotations_off_,
@@ -1860,7 +1865,7 @@ bool DexFileVerifier::CheckInterSectionIterate(uint32_t offset, uint32_t count,
break;
}
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1908,7 +1913,7 @@ bool DexFileVerifier::CheckInterSection() {
break;
}
default:
- LOG(ERROR) << StringPrintf("Unknown map item type %x", type);
+ ErrorStringPrintf("Unknown map item type %x", type);
return false;
}
@@ -1942,4 +1947,13 @@ bool DexFileVerifier::Verify() {
return true;
}
+void DexFileVerifier::ErrorStringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ DCHECK(failure_reason_.empty()) << failure_reason_;
+ failure_reason_ = StringPrintf("Failure to verify dex file '%s': ", location_);
+ StringAppendV(&failure_reason_, fmt, ap);
+ va_end(ap);
+}
+
} // namespace art
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 3797dc77e5..4b8b80ae7b 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -24,29 +24,35 @@ namespace art {
class DexFileVerifier {
public:
- static bool Verify(const DexFile* dex_file, const byte* begin, size_t size);
+ static bool Verify(const DexFile* dex_file, const byte* begin, size_t size,
+ const char* location, std::string* error_msg);
+
+ const std::string& FailureReason() const {
+ return failure_reason_;
+ }
private:
- DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size)
- : dex_file_(dex_file), begin_(begin), size_(size),
+ DexFileVerifier(const DexFile* dex_file, const byte* begin, size_t size, const char* location)
+ : dex_file_(dex_file), begin_(begin), size_(size), location_(location),
header_(&dex_file->GetHeader()), ptr_(NULL), previous_item_(NULL) {
}
bool Verify();
- bool CheckPointerRange(const void* start, const void* end, const char* label) const;
- bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label) const;
- bool CheckIndex(uint32_t field, uint32_t limit, const char* label) const;
+ bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
+ bool CheckPointerRange(const void* start, const void* end, const char* label);
+ bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label);
+ bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
- bool CheckHeader() const;
- bool CheckMap() const;
+ bool CheckHeader();
+ bool CheckMap();
uint32_t ReadUnsignedLittleEndian(uint32_t size);
bool CheckAndGetHandlerOffsets(const DexFile::CodeItem* code_item,
- uint32_t* handler_offsets, uint32_t handlers_size);
- bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static) const;
+ uint32_t* handler_offsets, uint32_t handlers_size);
+ bool CheckClassDataItemField(uint32_t idx, uint32_t access_flags, bool expect_static);
bool CheckClassDataItemMethod(uint32_t idx, uint32_t access_flags, uint32_t code_offset,
- bool expect_direct) const;
+ bool expect_direct);
bool CheckPadding(uint32_t offset, uint32_t aligned_offset);
bool CheckEncodedValue();
bool CheckEncodedArray();
@@ -82,14 +88,20 @@ class DexFileVerifier {
bool CheckInterSectionIterate(uint32_t offset, uint32_t count, uint16_t type);
bool CheckInterSection();
- const DexFile* dex_file_;
- const byte* begin_;
- size_t size_;
- const DexFile::Header* header_;
+ void ErrorStringPrintf(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
+
+ const DexFile* const dex_file_;
+ const byte* const begin_;
+ const size_t size_;
+ const char* const location_;
+ const DexFile::Header* const header_;
SafeMap<uint32_t, uint16_t> offset_to_type_map_;
const byte* ptr_;
const void* previous_item_;
+
+ std::string failure_reason_;
};
} // namespace art
diff --git a/runtime/dex_instruction-inl.h b/runtime/dex_instruction-inl.h
index 6e21273358..207b0b6795 100644
--- a/runtime/dex_instruction-inl.h
+++ b/runtime/dex_instruction-inl.h
@@ -24,29 +24,29 @@ namespace art {
//------------------------------------------------------------------------------
// VRegA
//------------------------------------------------------------------------------
-inline int8_t Instruction::VRegA_10t() const {
+inline int8_t Instruction::VRegA_10t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k10t);
- return static_cast<int8_t>(InstAA());
+ return static_cast<int8_t>(InstAA(inst_data));
}
-inline uint8_t Instruction::VRegA_10x() const {
+inline uint8_t Instruction::VRegA_10x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k10x);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint4_t Instruction::VRegA_11n() const {
+inline uint4_t Instruction::VRegA_11n(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k11n);
- return InstA();
+ return InstA(inst_data);
}
-inline uint8_t Instruction::VRegA_11x() const {
+inline uint8_t Instruction::VRegA_11x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k11x);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint4_t Instruction::VRegA_12x() const {
+inline uint4_t Instruction::VRegA_12x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k12x);
- return InstA();
+ return InstA(inst_data);
}
inline int16_t Instruction::VRegA_20t() const {
@@ -54,54 +54,54 @@ inline int16_t Instruction::VRegA_20t() const {
return static_cast<int16_t>(Fetch16(1));
}
-inline uint8_t Instruction::VRegA_21c() const {
+inline uint8_t Instruction::VRegA_21c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21c);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_21h() const {
+inline uint8_t Instruction::VRegA_21h(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21h);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_21s() const {
+inline uint8_t Instruction::VRegA_21s(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21s);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_21t() const {
+inline uint8_t Instruction::VRegA_21t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k21t);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_22b() const {
+inline uint8_t Instruction::VRegA_22b(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22b);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint4_t Instruction::VRegA_22c() const {
+inline uint4_t Instruction::VRegA_22c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22c);
- return InstA();
+ return InstA(inst_data);
}
-inline uint4_t Instruction::VRegA_22s() const {
+inline uint4_t Instruction::VRegA_22s(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22s);
- return InstA();
+ return InstA(inst_data);
}
-inline uint4_t Instruction::VRegA_22t() const {
+inline uint4_t Instruction::VRegA_22t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22t);
- return InstA();
+ return InstA(inst_data);
}
-inline uint8_t Instruction::VRegA_22x() const {
+inline uint8_t Instruction::VRegA_22x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22x);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_23x() const {
+inline uint8_t Instruction::VRegA_23x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k23x);
- return InstAA();
+ return InstAA(inst_data);
}
inline int32_t Instruction::VRegA_30t() const {
@@ -109,19 +109,19 @@ inline int32_t Instruction::VRegA_30t() const {
return static_cast<int32_t>(Fetch32(1));
}
-inline uint8_t Instruction::VRegA_31c() const {
+inline uint8_t Instruction::VRegA_31c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k31c);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_31i() const {
+inline uint8_t Instruction::VRegA_31i(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k31i);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_31t() const {
+inline uint8_t Instruction::VRegA_31t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k31t);
- return InstAA();
+ return InstAA(inst_data);
}
inline uint16_t Instruction::VRegA_32x() const {
@@ -129,32 +129,32 @@ inline uint16_t Instruction::VRegA_32x() const {
return Fetch16(1);
}
-inline uint4_t Instruction::VRegA_35c() const {
+inline uint4_t Instruction::VRegA_35c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k35c);
- return InstB(); // This is labeled A in the spec.
+ return InstB(inst_data); // This is labeled A in the spec.
}
-inline uint8_t Instruction::VRegA_3rc() const {
+inline uint8_t Instruction::VRegA_3rc(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k3rc);
- return InstAA();
+ return InstAA(inst_data);
}
-inline uint8_t Instruction::VRegA_51l() const {
+inline uint8_t Instruction::VRegA_51l(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k51l);
- return InstAA();
+ return InstAA(inst_data);
}
//------------------------------------------------------------------------------
// VRegB
//------------------------------------------------------------------------------
-inline int4_t Instruction::VRegB_11n() const {
+inline int4_t Instruction::VRegB_11n(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k11n);
- return static_cast<int4_t>((InstB() << 28) >> 28);
+ return static_cast<int4_t>((InstB(inst_data) << 28) >> 28);
}
-inline uint4_t Instruction::VRegB_12x() const {
+inline uint4_t Instruction::VRegB_12x(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k12x);
- return InstB();
+ return InstB(inst_data);
}
inline uint16_t Instruction::VRegB_21c() const {
@@ -182,19 +182,19 @@ inline uint8_t Instruction::VRegB_22b() const {
return static_cast<uint8_t>(Fetch16(1) & 0xff);
}
-inline uint4_t Instruction::VRegB_22c() const {
+inline uint4_t Instruction::VRegB_22c(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22c);
- return InstB();
+ return InstB(inst_data);
}
-inline uint4_t Instruction::VRegB_22s() const {
+inline uint4_t Instruction::VRegB_22s(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22s);
- return InstB();
+ return InstB(inst_data);
}
-inline uint4_t Instruction::VRegB_22t() const {
+inline uint4_t Instruction::VRegB_22t(uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k22t);
- return InstB();
+ return InstB(inst_data);
}
inline uint16_t Instruction::VRegB_22x() const {
@@ -281,7 +281,7 @@ inline uint16_t Instruction::VRegC_3rc() const {
return Fetch16(2);
}
-inline void Instruction::GetArgs(uint32_t arg[5]) const {
+inline void Instruction::GetArgs(uint32_t arg[5], uint16_t inst_data) const {
DCHECK_EQ(FormatOf(Opcode()), k35c);
/*
@@ -295,7 +295,8 @@ inline void Instruction::GetArgs(uint32_t arg[5]) const {
* method constant (or equivalent) is always in vB.
*/
uint16_t regList = Fetch16(2);
- uint4_t count = InstB(); // This is labeled A in the spec.
+ uint4_t count = InstB(inst_data); // This is labeled A in the spec.
+ DCHECK_LE(count, 5U) << "Invalid arg count in 35c (" << count << ")";
/*
* Copy the argument registers into the arg[] array, and
@@ -305,15 +306,13 @@ inline void Instruction::GetArgs(uint32_t arg[5]) const {
* copies of those.) Note that cases 5..2 fall through.
*/
switch (count) {
- case 5: arg[4] = InstA();
+ case 5: arg[4] = InstA(inst_data);
case 4: arg[3] = (regList >> 12) & 0x0f;
case 3: arg[2] = (regList >> 8) & 0x0f;
case 2: arg[1] = (regList >> 4) & 0x0f;
case 1: arg[0] = regList & 0x0f; break;
- case 0: break; // Valid, but no need to do anything.
- default:
- LOG(ERROR) << "Invalid arg count in 35c (" << count << ")";
- return;
+ default: // case 0
+ break; // Valid, but no need to do anything.
}
}
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index 13b0f1c270..c434cdd938 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -217,44 +217,122 @@ class Instruction {
// VRegA
bool HasVRegA() const;
int32_t VRegA() const;
- int8_t VRegA_10t() const;
- uint8_t VRegA_10x() const;
- uint4_t VRegA_11n() const;
- uint8_t VRegA_11x() const;
- uint4_t VRegA_12x() const;
+
+ int8_t VRegA_10t() const {
+ return VRegA_10t(Fetch16(0));
+ }
+ uint8_t VRegA_10x() const {
+ return VRegA_10x(Fetch16(0));
+ }
+ uint4_t VRegA_11n() const {
+ return VRegA_11n(Fetch16(0));
+ }
+ uint8_t VRegA_11x() const {
+ return VRegA_11x(Fetch16(0));
+ }
+ uint4_t VRegA_12x() const {
+ return VRegA_12x(Fetch16(0));
+ }
int16_t VRegA_20t() const;
- uint8_t VRegA_21c() const;
- uint8_t VRegA_21h() const;
- uint8_t VRegA_21s() const;
- uint8_t VRegA_21t() const;
- uint8_t VRegA_22b() const;
- uint4_t VRegA_22c() const;
- uint4_t VRegA_22s() const;
- uint4_t VRegA_22t() const;
- uint8_t VRegA_22x() const;
- uint8_t VRegA_23x() const;
+ uint8_t VRegA_21c() const {
+ return VRegA_21c(Fetch16(0));
+ }
+ uint8_t VRegA_21h() const {
+ return VRegA_21h(Fetch16(0));
+ }
+ uint8_t VRegA_21s() const {
+ return VRegA_21s(Fetch16(0));
+ }
+ uint8_t VRegA_21t() const {
+ return VRegA_21t(Fetch16(0));
+ }
+ uint8_t VRegA_22b() const {
+ return VRegA_22b(Fetch16(0));
+ }
+ uint4_t VRegA_22c() const {
+ return VRegA_22c(Fetch16(0));
+ }
+ uint4_t VRegA_22s() const {
+ return VRegA_22s(Fetch16(0));
+ }
+ uint4_t VRegA_22t() const {
+ return VRegA_22t(Fetch16(0));
+ }
+ uint8_t VRegA_22x() const {
+ return VRegA_22x(Fetch16(0));
+ }
+ uint8_t VRegA_23x() const {
+ return VRegA_23x(Fetch16(0));
+ }
int32_t VRegA_30t() const;
- uint8_t VRegA_31c() const;
- uint8_t VRegA_31i() const;
- uint8_t VRegA_31t() const;
+ uint8_t VRegA_31c() const {
+ return VRegA_31c(Fetch16(0));
+ }
+ uint8_t VRegA_31i() const {
+ return VRegA_31i(Fetch16(0));
+ }
+ uint8_t VRegA_31t() const {
+ return VRegA_31t(Fetch16(0));
+ }
uint16_t VRegA_32x() const;
- uint4_t VRegA_35c() const;
- uint8_t VRegA_3rc() const;
- uint8_t VRegA_51l() const;
+ uint4_t VRegA_35c() const {
+ return VRegA_35c(Fetch16(0));
+ }
+ uint8_t VRegA_3rc() const {
+ return VRegA_3rc(Fetch16(0));
+ }
+ uint8_t VRegA_51l() const {
+ return VRegA_51l(Fetch16(0));
+ }
+
+ // The following methods return the vA operand for various instruction formats. The "inst_data"
+ // parameter holds the first 16 bits of instruction which the returned value is decoded from.
+ int8_t VRegA_10t(uint16_t inst_data) const;
+ uint8_t VRegA_10x(uint16_t inst_data) const;
+ uint4_t VRegA_11n(uint16_t inst_data) const;
+ uint8_t VRegA_11x(uint16_t inst_data) const;
+ uint4_t VRegA_12x(uint16_t inst_data) const;
+ uint8_t VRegA_21c(uint16_t inst_data) const;
+ uint8_t VRegA_21h(uint16_t inst_data) const;
+ uint8_t VRegA_21s(uint16_t inst_data) const;
+ uint8_t VRegA_21t(uint16_t inst_data) const;
+ uint8_t VRegA_22b(uint16_t inst_data) const;
+ uint4_t VRegA_22c(uint16_t inst_data) const;
+ uint4_t VRegA_22s(uint16_t inst_data) const;
+ uint4_t VRegA_22t(uint16_t inst_data) const;
+ uint8_t VRegA_22x(uint16_t inst_data) const;
+ uint8_t VRegA_23x(uint16_t inst_data) const;
+ uint8_t VRegA_31c(uint16_t inst_data) const;
+ uint8_t VRegA_31i(uint16_t inst_data) const;
+ uint8_t VRegA_31t(uint16_t inst_data) const;
+ uint4_t VRegA_35c(uint16_t inst_data) const;
+ uint8_t VRegA_3rc(uint16_t inst_data) const;
+ uint8_t VRegA_51l(uint16_t inst_data) const;
// VRegB
bool HasVRegB() const;
int32_t VRegB() const;
- int4_t VRegB_11n() const;
- uint4_t VRegB_12x() const;
+
+ int4_t VRegB_11n() const {
+ return VRegB_11n(Fetch16(0));
+ }
+ uint4_t VRegB_12x() const {
+ return VRegB_12x(Fetch16(0));
+ }
uint16_t VRegB_21c() const;
uint16_t VRegB_21h() const;
int16_t VRegB_21s() const;
int16_t VRegB_21t() const;
uint8_t VRegB_22b() const;
- uint4_t VRegB_22c() const;
- uint4_t VRegB_22s() const;
- uint4_t VRegB_22t() const;
+ uint4_t VRegB_22c() const {
+ return VRegB_22c(Fetch16(0));
+ }
+ uint4_t VRegB_22s() const {
+ return VRegB_22s(Fetch16(0));
+ }
+ uint4_t VRegB_22t() const {
+ return VRegB_22t(Fetch16(0));
+ }
uint16_t VRegB_22x() const;
uint8_t VRegB_23x() const;
uint32_t VRegB_31c() const;
@@ -265,9 +343,19 @@ class Instruction {
uint16_t VRegB_3rc() const;
uint64_t VRegB_51l() const; // vB_wide
+ // The following methods return the vB operand for all instruction formats where it is encoded in
+ // the first 16 bits of instruction. The "inst_data" parameter holds these 16 bits. The returned
+ // value is decoded from it.
+ int4_t VRegB_11n(uint16_t inst_data) const;
+ uint4_t VRegB_12x(uint16_t inst_data) const;
+ uint4_t VRegB_22c(uint16_t inst_data) const;
+ uint4_t VRegB_22s(uint16_t inst_data) const;
+ uint4_t VRegB_22t(uint16_t inst_data) const;
+
// VRegC
bool HasVRegC() const;
int32_t VRegC() const;
+
int8_t VRegC_22b() const;
uint16_t VRegC_22c() const;
int16_t VRegC_22s() const;
@@ -277,11 +365,21 @@ class Instruction {
uint16_t VRegC_3rc() const;
// Fills the given array with the 'arg' array of the instruction.
- void GetArgs(uint32_t args[5]) const;
+ void GetArgs(uint32_t args[5], uint16_t inst_data) const;
+ void GetArgs(uint32_t args[5]) const {
+ return GetArgs(args, Fetch16(0));
+ }
+
+ // Returns the opcode field of the instruction. The given "inst_data" parameter must be the first
+ // 16 bits of instruction.
+ Code Opcode(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<Code>(inst_data & 0xFF);
+ }
- // Returns the opcode field of the instruction.
+ // Returns the opcode field of the instruction from the first 16 bits of instruction.
Code Opcode() const {
- return static_cast<Code>(Fetch16(0) & 0xFF);
+ return Opcode(Fetch16(0));
}
void SetOpcode(Code opcode) {
@@ -395,28 +493,43 @@ class Instruction {
// Dump code_units worth of this instruction, padding to code_units for shorter instructions
std::string DumpHex(size_t code_units) const;
- private:
- size_t SizeInCodeUnitsComplexOpcode() const;
-
uint16_t Fetch16(size_t offset) const {
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
return insns[offset];
}
+ private:
+ size_t SizeInCodeUnitsComplexOpcode() const;
+
uint32_t Fetch32(size_t offset) const {
return (Fetch16(offset) | ((uint32_t) Fetch16(offset + 1) << 16));
}
uint4_t InstA() const {
- return static_cast<uint4_t>((Fetch16(0) >> 8) & 0x0f);
+ return InstA(Fetch16(0));
}
uint4_t InstB() const {
- return static_cast<uint4_t>(Fetch16(0) >> 12);
+ return InstB(Fetch16(0));
}
uint8_t InstAA() const {
- return static_cast<uint8_t>(Fetch16(0) >> 8);
+ return InstAA(Fetch16(0));
+ }
+
+ uint4_t InstA(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint4_t>((inst_data >> 8) & 0x0f);
+ }
+
+ uint4_t InstB(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint4_t>(inst_data >> 12);
+ }
+
+ uint8_t InstAA(uint16_t inst_data) const {
+ DCHECK_EQ(inst_data, Fetch16(0));
+ return static_cast<uint8_t>(inst_data >> 8);
}
static const char* const kInstructionNames[];
diff --git a/runtime/dex_method_iterator_test.cc b/runtime/dex_method_iterator_test.cc
index 64c645e81a..9961df9a55 100644
--- a/runtime/dex_method_iterator_test.cc
+++ b/runtime/dex_method_iterator_test.cc
@@ -20,16 +20,27 @@
namespace art {
-class DexMethodIteratorTest : public CommonTest {};
+class DexMethodIteratorTest : public CommonTest {
+ public:
+ const DexFile* OpenDexFile(const std::string& partial_filename) {
+ std::string dfn = GetDexFileName(partial_filename);
+ std::string error_msg;
+ const DexFile* dexfile = DexFile::Open(dfn.c_str(), dfn.c_str(), &error_msg);
+ if (dexfile == nullptr) {
+ LG << "Failed to open '" << dfn << "': " << error_msg;
+ }
+ return dexfile;
+ }
+};
TEST_F(DexMethodIteratorTest, Basic) {
ScopedObjectAccess soa(Thread::Current());
std::vector<const DexFile*> dex_files;
- dex_files.push_back(DexFile::Open(GetDexFileName("core"), GetDexFileName("core")));
- dex_files.push_back(DexFile::Open(GetDexFileName("conscrypt"), GetDexFileName("conscrypt")));
- dex_files.push_back(DexFile::Open(GetDexFileName("okhttp"), GetDexFileName("okhttp")));
- dex_files.push_back(DexFile::Open(GetDexFileName("core-junit"), GetDexFileName("core-junit")));
- dex_files.push_back(DexFile::Open(GetDexFileName("bouncycastle"), GetDexFileName("bouncycastle")));
+ dex_files.push_back(OpenDexFile("core"));
+ dex_files.push_back(OpenDexFile("conscrypt"));
+ dex_files.push_back(OpenDexFile("okhttp"));
+ dex_files.push_back(OpenDexFile("core-junit"));
+ dex_files.push_back(OpenDexFile("bouncycastle"));
DexMethodIterator it(dex_files);
while (it.HasNext()) {
const DexFile& dex_file = it.GetDexFile();
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index dfb6819a44..15c95f2206 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -40,15 +40,16 @@ ElfFile::ElfFile()
symtab_symbol_table_(NULL),
dynsym_symbol_table_(NULL) {}
-ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only) {
+ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
+ std::string* error_msg) {
UniquePtr<ElfFile> elf_file(new ElfFile());
- if (!elf_file->Setup(file, writable, program_header_only)) {
- return NULL;
+ if (!elf_file->Setup(file, writable, program_header_only, error_msg)) {
+ return nullptr;
}
return elf_file.release();
}
-bool ElfFile::Setup(File* file, bool writable, bool program_header_only) {
+bool ElfFile::Setup(File* file, bool writable, bool program_header_only, std::string* error_msg) {
CHECK(file != NULL);
file_ = file;
writable_ = writable;
@@ -66,40 +67,42 @@ bool ElfFile::Setup(File* file, bool writable, bool program_header_only) {
int64_t file_length = file_->GetLength();
if (file_length < 0) {
errno = -file_length;
- PLOG(WARNING) << "Failed to get length of file: " << file_->GetPath() << " fd=" << file_->Fd();
+ *error_msg = StringPrintf("Failed to get length of file: '%s' fd=%d: %s",
+ file_->GetPath().c_str(), file_->Fd(), strerror(errno));
return false;
}
if (file_length < sizeof(llvm::ELF::Elf32_Ehdr)) {
- if (writable) {
- LOG(WARNING) << "File size of " << file_length
- << " bytes not large enough to contain ELF header of "
- << sizeof(llvm::ELF::Elf32_Ehdr) << " bytes: " << file_->GetPath();
- }
+ *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF header of "
+ "%zd bytes: '%s'", file_length, sizeof(llvm::ELF::Elf32_Ehdr),
+ file_->GetPath().c_str());
return false;
}
if (program_header_only) {
// first just map ELF header to get program header size information
size_t elf_header_size = sizeof(llvm::ELF::Elf32_Ehdr);
- if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0))) {
+ if (!SetMap(MemMap::MapFile(elf_header_size, prot, flags, file_->Fd(), 0,
+ file_->GetPath().c_str(), error_msg))) {
return false;
}
// then remap to cover program header
size_t program_header_size = header_->e_phoff + (header_->e_phentsize * header_->e_phnum);
if (file_length < program_header_size) {
- LOG(WARNING) << "File size of " << file_length
- << " bytes not large enough to contain ELF program header of "
- << program_header_size << " bytes: " << file_->GetPath();
+ *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF program "
+ "header of %zd bytes: '%s'", file_length,
+ sizeof(llvm::ELF::Elf32_Ehdr), file_->GetPath().c_str());
return false;
}
- if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0))) {
- LOG(WARNING) << "Failed to map ELF program headers: " << file_->GetPath();
+ if (!SetMap(MemMap::MapFile(program_header_size, prot, flags, file_->Fd(), 0,
+ file_->GetPath().c_str(), error_msg))) {
+ *error_msg = StringPrintf("Failed to map ELF program headers: %s", error_msg->c_str());
return false;
}
} else {
// otherwise map entire file
- if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0))) {
- LOG(WARNING) << "Failed to map ELF file: " << file_->GetPath();
+ if (!SetMap(MemMap::MapFile(file_->GetLength(), prot, flags, file_->Fd(), 0,
+ file_->GetPath().c_str(), error_msg))) {
+ *error_msg = StringPrintf("Failed to map ELF file: %s", error_msg->c_str());
return false;
}
}
@@ -114,7 +117,8 @@ bool ElfFile::Setup(File* file, bool writable, bool program_header_only) {
// Find .dynamic section info from program header
dynamic_program_header_ = FindProgamHeaderByType(llvm::ELF::PT_DYNAMIC);
if (dynamic_program_header_ == NULL) {
- LOG(WARNING) << "Failed to find PT_DYNAMIC program header in ELF file: " << file_->GetPath();
+ *error_msg = StringPrintf("Failed to find PT_DYNAMIC program header in ELF file: '%s'",
+ file_->GetPath().c_str());
return false;
}
@@ -596,7 +600,7 @@ size_t ElfFile::GetLoadedSize() {
return loaded_size;
}
-bool ElfFile::Load(bool executable) {
+bool ElfFile::Load(bool executable, std::string* error_msg) {
// TODO: actually return false error
CHECK(program_header_only_) << file_->GetPath();
for (llvm::ELF::Elf32_Word i = 0; i < GetProgramHeaderNum(); i++) {
@@ -628,9 +632,10 @@ bool ElfFile::Load(bool executable) {
if (program_header.p_vaddr == 0) {
std::string reservation_name("ElfFile reservation for ");
reservation_name += file_->GetPath();
+ std::string error_msg;
UniquePtr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
- NULL, GetLoadedSize(), PROT_NONE));
- CHECK(reserve.get() != NULL) << file_->GetPath();
+ NULL, GetLoadedSize(), PROT_NONE, &error_msg));
+ CHECK(reserve.get() != NULL) << file_->GetPath() << ": " << error_msg;
base_address_ = reserve->Begin();
segments_.push_back(reserve.release());
}
@@ -657,18 +662,20 @@ bool ElfFile::Load(bool executable) {
flags |= MAP_PRIVATE;
}
if (file_length < (program_header.p_offset + program_header.p_memsz)) {
- LOG(WARNING) << "File size of " << file_length
- << " bytes not large enough to contain ELF segment " << i
- << " of " << (program_header.p_offset + program_header.p_memsz)
- << " bytes: " << file_->GetPath();
+ *error_msg = StringPrintf("File size of %lld bytes not large enough to contain ELF segment "
+ "%d of %d bytes: '%s'", file_length, i,
+ program_header.p_offset + program_header.p_memsz,
+ file_->GetPath().c_str());
return false;
}
UniquePtr<MemMap> segment(MemMap::MapFileAtAddress(p_vaddr,
program_header.p_memsz,
prot, flags, file_->Fd(),
program_header.p_offset,
- true));
- CHECK(segment.get() != NULL) << file_->GetPath();
+ true,
+ file_->GetPath().c_str(),
+ error_msg));
+ CHECK(segment.get() != nullptr) << *error_msg;
CHECK_EQ(segment->Begin(), p_vaddr) << file_->GetPath();
segments_.push_back(segment.release());
}
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 33b5fc3f9b..b0251370d2 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -35,7 +35,7 @@ namespace art {
// ELFObjectFile.
class ElfFile {
public:
- static ElfFile* Open(File* file, bool writable, bool program_header_only);
+ static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
~ElfFile();
// Load segments into memory based on PT_LOAD program headers
@@ -115,12 +115,12 @@ class ElfFile {
// Load segments into memory based on PT_LOAD program headers.
// executable is true at run time, false at compile time.
- bool Load(bool executable);
+ bool Load(bool executable, std::string* error_msg);
private:
ElfFile();
- bool Setup(File* file, bool writable, bool program_header_only);
+ bool Setup(File* file, bool writable, bool program_header_only, std::string* error_msg);
bool SetMap(MemMap* map);
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 52f8c81ab6..24ab1ce9da 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -33,20 +33,20 @@
namespace art {
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer,
- int32_t component_count, Thread* self,
- bool access_check) {
+static inline bool CheckFilledNewArrayAlloc(uint32_t type_idx, mirror::ArtMethod* referrer,
+ int32_t component_count, Thread* self,
+ bool access_check, mirror::Class** klass_ptr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (UNLIKELY(component_count < 0)) {
ThrowNegativeArraySizeException(component_count);
- return NULL; // Failure
+ return false; // Failure
}
- mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->Get(type_idx);
+ mirror::Class* klass = referrer->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, referrer);
if (klass == NULL) { // Error
DCHECK(self->IsExceptionPending());
- return NULL; // Failure
+ return false; // Failure
}
}
if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
@@ -60,213 +60,40 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod*
"Found type %s; filled-new-array not implemented for anything but \'int\'",
PrettyDescriptor(klass).c_str());
}
- return NULL; // Failure
- } else {
- if (access_check) {
- mirror::Class* referrer_klass = referrer->GetDeclaringClass();
- if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer_klass, klass);
- return NULL; // Failure
- }
- }
- DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
- return mirror::Array::Alloc(self, klass, component_count);
- }
-}
-
-mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer,
- Thread* self, FindFieldType type, size_t expected_size,
- bool access_check) {
- bool is_primitive;
- bool is_set;
- bool is_static;
- switch (type) {
- case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
- case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
- case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
- case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
- case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
- case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
- case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
- case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through.
- default: is_primitive = true; is_set = true; is_static = true; break;
- }
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
- if (UNLIKELY(resolved_field == NULL)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return NULL; // Failure.
+ return false; // Failure
}
- mirror::Class* fields_class = resolved_field->GetDeclaringClass();
if (access_check) {
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
- return NULL;
- }
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
- !referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- // The referring class can't access the resolved field, this may occur as a result of a
- // protected field being made public by a sub-class. Resort to the dex file to determine
- // the correct class for the access check.
- const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
- fields_class = class_linker->ResolveType(dex_file,
- dex_file.GetFieldId(field_idx).class_idx_,
- referring_class);
- if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
- ThrowIllegalAccessErrorClass(referring_class, fields_class);
- return NULL; // failure
- } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
- resolved_field->GetAccessFlags()))) {
- ThrowIllegalAccessErrorField(referring_class, resolved_field);
- return NULL; // failure
- }
- }
- if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
- return NULL; // failure
- } else {
- FieldHelper fh(resolved_field);
- if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
- fh.FieldSize() != expected_size)) {
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- DCHECK(throw_location.GetMethod() == referrer);
- self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
- "Attempted read of %zd-bit %s on field '%s'",
- expected_size * (32 / sizeof(int32_t)),
- is_primitive ? "primitive" : "non-primitive",
- PrettyField(resolved_field, true).c_str());
- return NULL; // failure
- }
+ mirror::Class* referrer_klass = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
+ ThrowIllegalAccessErrorClass(referrer_klass, klass);
+ return false; // Failure
}
}
- if (!is_static) {
- // instance fields must be being accessed on an initialized class
- return resolved_field;
- } else {
- // If the class is initialized we're done.
- if (fields_class->IsInitialized()) {
- return resolved_field;
- } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true, true)) {
- // Otherwise let's ensure the class is initialized before resolving the field.
- return resolved_field;
- } else {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind
- return NULL; // failure
- }
+ DCHECK(klass->IsArrayClass()) << PrettyClass(klass);
+ *klass_ptr = klass;
+ return true;
+}
+
+// Helper function to allocate array for FILLED_NEW_ARRAY.
+mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* referrer,
+ int32_t component_count, Thread* self,
+ bool access_check) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) {
+ return NULL;
}
+ return mirror::Array::AllocUninstrumented(self, klass, component_count);
}
-// Slow path method resolution
-mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* self, bool access_check, InvokeType type) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- bool is_direct = type == kStatic || type == kDirect;
- mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type);
- if (UNLIKELY(resolved_method == NULL)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return NULL; // Failure.
- } else if (UNLIKELY(this_object == NULL && type != kStatic)) {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowLocation throw_location = self->GetCurrentLocationForThrow();
- DCHECK(referrer == throw_location.GetMethod());
- ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
- return NULL; // Failure.
- } else {
- if (!access_check) {
- if (is_direct) {
- return resolved_method;
- } else if (type == kInterface) {
- mirror::ArtMethod* interface_method =
- this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == NULL)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
- referrer);
- return NULL; // Failure.
- } else {
- return interface_method;
- }
- } else {
- mirror::ObjectArray<mirror::ArtMethod>* vtable;
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (type == kSuper) {
- vtable = referrer->GetDeclaringClass()->GetSuperClass()->GetVTable();
- } else {
- vtable = this_object->GetClass()->GetVTable();
- }
- // TODO: eliminate bounds check?
- return vtable->Get(vtable_index);
- }
- } else {
- // Incompatible class change should have been handled in resolve method.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
- referrer);
- return NULL; // Failure.
- }
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // The referring class can't access the resolved method, this may occur as a result of a
- // protected method being made public by implementing an interface that re-declares the
- // method public. Resort to the dex file to determine the correct class for the access check
- const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
- methods_class = class_linker->ResolveType(dex_file,
- dex_file.GetMethodId(method_idx).class_idx_,
- referring_class);
- if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
- ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
- referrer, resolved_method, type);
- return NULL; // Failure.
- } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
- return NULL; // Failure.
- }
- }
- if (is_direct) {
- return resolved_method;
- } else if (type == kInterface) {
- mirror::ArtMethod* interface_method =
- this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
- if (UNLIKELY(interface_method == NULL)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
- referrer);
- return NULL; // Failure.
- } else {
- return interface_method;
- }
- } else {
- mirror::ObjectArray<mirror::ArtMethod>* vtable;
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (type == kSuper) {
- mirror::Class* super_class = referring_class->GetSuperClass();
- if (LIKELY(super_class != NULL)) {
- vtable = referring_class->GetSuperClass()->GetVTable();
- } else {
- vtable = NULL;
- }
- } else {
- vtable = this_object->GetClass()->GetVTable();
- }
- if (LIKELY(vtable != NULL &&
- vtable_index < static_cast<uint32_t>(vtable->GetLength()))) {
- return vtable->GetWithoutChecks(vtable_index);
- } else {
- // Behavior to agree with that of the verifier.
- MethodHelper mh(resolved_method);
- ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
- mh.GetSignature());
- return NULL; // Failure.
- }
- }
- }
+// Helper function to allocate array for FILLED_NEW_ARRAY.
+mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* referrer,
+ int32_t component_count, Thread* self,
+ bool access_check) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) {
+ return NULL;
}
+ return mirror::Array::AllocInstrumented(self, klass, component_count);
}
void ThrowStackOverflowError(Thread* self) {
@@ -405,5 +232,4 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessUnchecked& soa, const char
return zero;
}
}
-
} // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b58cb332c..7ce50c5dfb 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -16,7 +16,8 @@
#ifndef ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
#define ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
-#include "object_utils.h"
+
+#include "base/macros.h"
#include "class_linker.h"
#include "common_throws.h"
#include "dex_file.h"
@@ -27,6 +28,7 @@
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/throwable.h"
+#include "object_utils.h"
#include "thread.h"
@@ -38,21 +40,18 @@ namespace mirror {
class Object;
} // namespace mirror
-// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
-// cannot be resolved, throw an error. If it can, use it to create an instance.
-// When verification/compiler hasn't been able to verify access, optionally perform an access
-// check.
-static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method,
- Thread* self,
- bool access_check)
+static inline bool CheckObjectAlloc(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self,
+ bool access_check,
+ mirror::Class** klass_ptr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+ mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
Runtime* runtime = Runtime::Current();
if (UNLIKELY(klass == NULL)) {
klass = runtime->GetClassLinker()->ResolveType(type_idx, method);
if (klass == NULL) {
DCHECK(self->IsExceptionPending());
- return NULL; // Failure
+ return false; // Failure
}
}
if (access_check) {
@@ -60,40 +59,63 @@ static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::Art
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
PrettyDescriptor(klass).c_str());
- return NULL; // Failure
+ return false; // Failure
}
mirror::Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
ThrowIllegalAccessErrorClass(referrer, klass);
- return NULL; // Failure
+ return false; // Failure
}
}
if (!klass->IsInitialized() &&
!runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) {
DCHECK(self->IsExceptionPending());
- return NULL; // Failure
+ return false; // Failure
}
- return klass->AllocObject(self);
+ *klass_ptr = klass;
+ return true;
}
-// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
-// it cannot be resolved, throw an error. If it can, use it to create an array.
+// Given the context of a calling Method, use its DexCache to resolve a type to a Class. If it
+// cannot be resolved, throw an error. If it can, use it to create an instance.
// When verification/compiler hasn't been able to verify access, optionally perform an access
// check.
-static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
- int32_t component_count,
- Thread* self, bool access_check)
+static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self,
+ bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) {
+ return NULL;
+ }
+ return klass->AllocObjectUninstrumented(self);
+}
+
+static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self,
+ bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) {
+ return NULL;
+ }
+ return klass->AllocObjectInstrumented(self);
+}
+
+static inline bool CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ bool access_check, mirror::Class** klass_ptr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (UNLIKELY(component_count < 0)) {
ThrowNegativeArraySizeException(component_count);
- return NULL; // Failure
+ return false; // Failure
}
- mirror::Class* klass = method->GetDexCacheResolvedTypes()->Get(type_idx);
+ mirror::Class* klass = method->GetDexCacheResolvedTypes()->GetWithoutChecks(type_idx);
if (UNLIKELY(klass == NULL)) { // Not in dex cache so try to resolve
klass = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method);
if (klass == NULL) { // Error
DCHECK(Thread::Current()->IsExceptionPending());
- return NULL; // Failure
+ return false; // Failure
}
CHECK(klass->IsArrayClass()) << PrettyClass(klass);
}
@@ -101,10 +123,37 @@ static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMe
mirror::Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
ThrowIllegalAccessErrorClass(referrer, klass);
- return NULL; // Failure
+ return false; // Failure
}
}
- return mirror::Array::Alloc(self, klass, component_count);
+ *klass_ptr = klass;
+ return true;
+}
+
+// Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If
+// it cannot be resolved, throw an error. If it can, use it to create an array.
+// When verification/compiler hasn't been able to verify access, optionally perform an access
+// check.
+static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self, bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) {
+ return NULL;
+ }
+ return mirror::Array::AllocUninstrumented(self, klass, component_count);
+}
+
+static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self, bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::Class* klass;
+ if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) {
+ return NULL;
+ }
+ return mirror::Array::AllocInstrumented(self, klass, component_count);
}
extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method,
@@ -112,6 +161,11 @@ extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtM
Thread* self, bool access_check)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+extern mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self, bool access_check)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Type of find field operation for fast and slow case.
enum FindFieldType {
InstanceObjectRead,
@@ -124,11 +178,242 @@ enum FindFieldType {
StaticPrimitiveWrite,
};
-// Slow field find that can initialize classes and may throw exceptions.
-extern mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer,
- Thread* self, FindFieldType type, size_t expected_size,
- bool access_check)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+template<FindFieldType type, bool access_check>
+static inline mirror::ArtField* FindFieldFromCode(uint32_t field_idx, const mirror::ArtMethod* referrer,
+ Thread* self, size_t expected_size) {
+ bool is_primitive;
+ bool is_set;
+ bool is_static;
+ switch (type) {
+ case InstanceObjectRead: is_primitive = false; is_set = false; is_static = false; break;
+ case InstanceObjectWrite: is_primitive = false; is_set = true; is_static = false; break;
+ case InstancePrimitiveRead: is_primitive = true; is_set = false; is_static = false; break;
+ case InstancePrimitiveWrite: is_primitive = true; is_set = true; is_static = false; break;
+ case StaticObjectRead: is_primitive = false; is_set = false; is_static = true; break;
+ case StaticObjectWrite: is_primitive = false; is_set = true; is_static = true; break;
+ case StaticPrimitiveRead: is_primitive = true; is_set = false; is_static = true; break;
+ case StaticPrimitiveWrite: // Keep GCC happy by having a default handler, fall-through.
+ default: is_primitive = true; is_set = true; is_static = true; break;
+ }
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::ArtField* resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
+ if (UNLIKELY(resolved_field == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ }
+ mirror::Class* fields_class = resolved_field->GetDeclaringClass();
+ if (access_check) {
+ if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+ ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
+ return nullptr;
+ }
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
+ !referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()))) {
+ // The referring class can't access the resolved field, this may occur as a result of a
+ // protected field being made public by a sub-class. Resort to the dex file to determine
+ // the correct class for the access check.
+ const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
+ fields_class = class_linker->ResolveType(dex_file,
+ dex_file.GetFieldId(field_idx).class_idx_,
+ referring_class);
+ if (UNLIKELY(!referring_class->CanAccess(fields_class))) {
+ ThrowIllegalAccessErrorClass(referring_class, fields_class);
+ return nullptr; // failure
+ } else if (UNLIKELY(!referring_class->CanAccessMember(fields_class,
+ resolved_field->GetAccessFlags()))) {
+ ThrowIllegalAccessErrorField(referring_class, resolved_field);
+ return nullptr; // failure
+ }
+ }
+ if (UNLIKELY(is_set && resolved_field->IsFinal() && (fields_class != referring_class))) {
+ ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
+ return nullptr; // failure
+ } else {
+ FieldHelper fh(resolved_field);
+ if (UNLIKELY(fh.IsPrimitiveType() != is_primitive ||
+ fh.FieldSize() != expected_size)) {
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ DCHECK(throw_location.GetMethod() == referrer);
+ self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+ "Attempted read of %zd-bit %s on field '%s'",
+ expected_size * (32 / sizeof(int32_t)),
+ is_primitive ? "primitive" : "non-primitive",
+ PrettyField(resolved_field, true).c_str());
+ return nullptr; // failure
+ }
+ }
+ }
+ if (!is_static) {
+ // instance fields must be being accessed on an initialized class
+ return resolved_field;
+ } else {
+ // If the class is initialized we're done.
+ if (LIKELY(fields_class->IsInitialized())) {
+ return resolved_field;
+ } else if (LIKELY(class_linker->EnsureInitialized(fields_class, true, true))) {
+ // Otherwise let's ensure the class is initialized before resolving the field.
+ return resolved_field;
+ } else {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind
+ return nullptr; // failure
+ }
+ }
+}
+
+// Explicit template declarations of FindFieldFromCode for all field access types.
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+static mirror::ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
+ const mirror::ArtMethod* referrer, \
+ Thread* self, size_t expected_size) \
+
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
+
+#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+
+template<InvokeType type, bool access_check>
+static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* referrer, Thread* self) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::ArtMethod* resolved_method = class_linker->ResolveMethod(method_idx, referrer, type);
+ if (UNLIKELY(resolved_method == nullptr)) {
+ DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
+ return nullptr; // Failure.
+ } else if (UNLIKELY(this_object == nullptr && type != kStatic)) {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ DCHECK(referrer == throw_location.GetMethod());
+ ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
+ return nullptr; // Failure.
+ } else if (access_check) {
+ // Incompatible class change should have been handled in resolve method.
+ if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+ ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
+ referrer);
+ return nullptr; // Failure.
+ }
+ mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+ mirror::Class* referring_class = referrer->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
+ !referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ // The referring class can't access the resolved method, this may occur as a result of a
+ // protected method being made public by implementing an interface that re-declares the
+ // method public. Resort to the dex file to determine the correct class for the access check
+ const DexFile& dex_file = *referring_class->GetDexCache()->GetDexFile();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ methods_class = class_linker->ResolveType(dex_file,
+ dex_file.GetMethodId(method_idx).class_idx_,
+ referring_class);
+ if (UNLIKELY(!referring_class->CanAccess(methods_class))) {
+ ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class,
+ referrer, resolved_method, type);
+ return nullptr; // Failure.
+ } else if (UNLIKELY(!referring_class->CanAccessMember(methods_class,
+ resolved_method->GetAccessFlags()))) {
+ ThrowIllegalAccessErrorMethod(referring_class, resolved_method);
+ return nullptr; // Failure.
+ }
+ }
+ }
+ switch (type) {
+ case kStatic:
+ case kDirect:
+ return resolved_method;
+ case kVirtual: {
+ mirror::ObjectArray<mirror::ArtMethod>* vtable = this_object->GetClass()->GetVTable();
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ if (access_check &&
+ (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) {
+ // Behavior to agree with that of the verifier.
+ MethodHelper mh(resolved_method);
+ ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
+ mh.GetSignature());
+ return nullptr; // Failure.
+ }
+ DCHECK(vtable != nullptr);
+ return vtable->GetWithoutChecks(vtable_index);
+ }
+ case kSuper: {
+ mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass();
+ uint16_t vtable_index = resolved_method->GetMethodIndex();
+ mirror::ObjectArray<mirror::ArtMethod>* vtable;
+ if (access_check) {
+ // Check existence of super class.
+ vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr;
+ if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) {
+ // Behavior to agree with that of the verifier.
+ MethodHelper mh(resolved_method);
+ ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), mh.GetName(),
+ mh.GetSignature());
+ return nullptr; // Failure.
+ }
+ } else {
+ // Super class must exist.
+ DCHECK(super_class != nullptr);
+ vtable = super_class->GetVTable();
+ }
+ DCHECK(vtable != nullptr);
+ return vtable->GetWithoutChecks(vtable_index);
+ }
+ case kInterface: {
+ uint32_t imt_index = resolved_method->GetDexMethodIndex() % ClassLinker::kImtSize;
+ mirror::ObjectArray<mirror::ArtMethod>* imt_table = this_object->GetClass()->GetImTable();
+ mirror::ArtMethod* imt_method = imt_table->Get(imt_index);
+ if (!imt_method->IsImtConflictMethod()) {
+ return imt_method;
+ } else {
+ mirror::ArtMethod* interface_method =
+ this_object->GetClass()->FindVirtualMethodForInterface(resolved_method);
+ if (UNLIKELY(interface_method == nullptr)) {
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method, this_object,
+ referrer);
+ return nullptr; // Failure.
+ } else {
+ return interface_method;
+ }
+ }
+ }
+ default:
+ LOG(FATAL) << "Unknown invoke type " << type;
+ return nullptr; // Failure.
+ }
+}
+
+// Explicit template declarations of FindMethodFromCode for all invoke types.
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static mirror::ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* referrer, \
+ Thread* self)
+#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
// Fast path field resolution that can't initialize classes or throw exceptions.
static inline mirror::ArtField* FindFieldFast(uint32_t field_idx,
@@ -228,11 +513,6 @@ static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx,
}
}
-extern mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object* this_object,
- mirror::ArtMethod* referrer,
- Thread* self, bool access_check, InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx,
const mirror::ArtMethod* referrer,
Thread* self, bool can_run_clinit,
@@ -258,7 +538,7 @@ static inline mirror::Class* ResolveVerifyAndClinit(uint32_t type_idx,
//
// Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
// running.
- if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
+ if (klass == referring_class && referrer->IsConstructor() && referrer->IsStatic()) {
return klass;
}
if (!class_linker->EnsureInitialized(klass, true, true)) {
@@ -324,7 +604,6 @@ static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mut
for (;;) {
if (thread->ReadFlag(kCheckpointRequest)) {
thread->RunCheckpointFunction();
- thread->AtomicClearFlag(kCheckpointRequest);
} else if (thread->ReadFlag(kSuspendRequest)) {
thread->FullSuspendCheck();
} else {
@@ -393,6 +672,23 @@ static inline const void* GetResolutionTrampoline(ClassLinker* class_linker) {
#endif
}
+static inline const void* GetPortableImtConflictTrampoline(ClassLinker* class_linker) {
+ return class_linker->GetPortableImtConflictTrampoline();
+}
+
+static inline const void* GetQuickImtConflictTrampoline(ClassLinker* class_linker) {
+ return class_linker->GetQuickImtConflictTrampoline();
+}
+
+// Return address of imt conflict trampoline stub for defined compiler.
+static inline const void* GetImtConflictTrampoline(ClassLinker* class_linker) {
+#if defined(ART_USE_PORTABLE_COMPILER)
+ return GetPortableImtConflictTrampoline(class_linker);
+#else
+ return GetQuickImtConflictTrampoline(class_linker);
+#endif
+}
+
extern "C" void art_portable_proxy_invoke_handler();
static inline const void* GetPortableProxyInvokeHandler() {
return reinterpret_cast<void*>(art_portable_proxy_invoke_handler);
@@ -416,6 +712,23 @@ static inline void* GetJniDlsymLookupStub() {
return reinterpret_cast<void*>(art_jni_dlsym_lookup_stub);
}
+template <typename INT_TYPE, typename FLOAT_TYPE>
+static inline INT_TYPE art_float_to_integral(FLOAT_TYPE f) {
+ const INT_TYPE kMaxInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::max());
+ const INT_TYPE kMinInt = static_cast<INT_TYPE>(std::numeric_limits<INT_TYPE>::min());
+ const FLOAT_TYPE kMaxIntAsFloat = static_cast<FLOAT_TYPE>(kMaxInt);
+ const FLOAT_TYPE kMinIntAsFloat = static_cast<FLOAT_TYPE>(kMinInt);
+ if (LIKELY(f > kMinIntAsFloat)) {
+ if (LIKELY(f < kMaxIntAsFloat)) {
+ return static_cast<INT_TYPE>(f);
+ } else {
+ return kMaxInt;
+ }
+ } else {
+ return (f != f) ? 0 : kMinInt; // f != f implies NaN
+ }
+}
+
} // namespace art
#endif // ART_RUNTIME_ENTRYPOINTS_ENTRYPOINT_UTILS_H_
diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
index ecf98bc722..05c02f22fa 100644
--- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
+++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc
@@ -31,7 +31,15 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m
mirror::ArtMethod* method = shadow_frame->GetMethod();
// Ensure static methods are initialized.
if (method->IsStatic()) {
- Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(), true, true);
+ mirror::Class* declaringClass = method->GetDeclaringClass();
+ if (UNLIKELY(!declaringClass->IsInitializing())) {
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass,
+ true, true))) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return;
+ }
+ CHECK(declaringClass->IsInitializing());
+ }
}
uint16_t arg_offset = (code_item == NULL) ? 0 : code_item->registers_size_ - code_item->ins_size_;
#if defined(ART_USE_PORTABLE_COMPILER)
@@ -40,7 +48,7 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m
method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()[0]);
#else
method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
- (shadow_frame->NumberOfVRegs() - arg_offset) * 4,
+ (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
result, mh.GetShorty()[0]);
#endif
}
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index 83d3a584c5..16364fcdb7 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -41,7 +41,7 @@ extern "C" void* artFindNativeMethod() {
return NULL;
} else {
// Register so that future calls don't come here
- method->RegisterNative(self, native_code);
+ method->RegisterNative(self, native_code, false);
return native_code;
}
}
@@ -115,7 +115,7 @@ extern "C" const void* artWorkAroundAppJniBugs(Thread* self, intptr_t* sp)
const void* code = reinterpret_cast<const void*>(jni_method->GetNativeGcMap());
if (UNLIKELY(code == NULL)) {
code = GetJniDlsymLookupStub();
- jni_method->RegisterNative(self, code);
+ jni_method->RegisterNative(self, code, false);
}
return code;
}
diff --git a/runtime/entrypoints/math_entrypoints.cc b/runtime/entrypoints/math_entrypoints.cc
index 31d13c8cd5..b839b6317d 100644
--- a/runtime/entrypoints/math_entrypoints.cc
+++ b/runtime/entrypoints/math_entrypoints.cc
@@ -16,6 +16,8 @@
#include "math_entrypoints.h"
+#include "entrypoint_utils.h"
+
namespace art {
extern "C" double art_l2d(int64_t l) {
@@ -31,59 +33,19 @@ extern "C" float art_l2f(int64_t l) {
* target doesn't support this normally, use these.
*/
extern "C" int64_t art_d2l(double d) {
- static const double kMaxLong = static_cast<double>(static_cast<int64_t>(0x7fffffffffffffffULL));
- static const double kMinLong = static_cast<double>(static_cast<int64_t>(0x8000000000000000ULL));
- if (d >= kMaxLong) {
- return static_cast<int64_t>(0x7fffffffffffffffULL);
- } else if (d <= kMinLong) {
- return static_cast<int64_t>(0x8000000000000000ULL);
- } else if (d != d) { // NaN case
- return 0;
- } else {
- return static_cast<int64_t>(d);
- }
+ return art_float_to_integral<int64_t, double>(d);
}
extern "C" int64_t art_f2l(float f) {
- static const float kMaxLong = static_cast<float>(static_cast<int64_t>(0x7fffffffffffffffULL));
- static const float kMinLong = static_cast<float>(static_cast<int64_t>(0x8000000000000000ULL));
- if (f >= kMaxLong) {
- return static_cast<int64_t>(0x7fffffffffffffffULL);
- } else if (f <= kMinLong) {
- return static_cast<int64_t>(0x8000000000000000ULL);
- } else if (f != f) { // NaN case
- return 0;
- } else {
- return static_cast<int64_t>(f);
- }
+ return art_float_to_integral<int64_t, float>(f);
}
extern "C" int32_t art_d2i(double d) {
- static const double kMaxInt = static_cast<double>(static_cast<int32_t>(0x7fffffffUL));
- static const double kMinInt = static_cast<double>(static_cast<int32_t>(0x80000000UL));
- if (d >= kMaxInt) {
- return static_cast<int32_t>(0x7fffffffUL);
- } else if (d <= kMinInt) {
- return static_cast<int32_t>(0x80000000UL);
- } else if (d != d) { // NaN case
- return 0;
- } else {
- return static_cast<int32_t>(d);
- }
+ return art_float_to_integral<int32_t, double>(d);
}
extern "C" int32_t art_f2i(float f) {
- static const float kMaxInt = static_cast<float>(static_cast<int32_t>(0x7fffffffUL));
- static const float kMinInt = static_cast<float>(static_cast<int32_t>(0x80000000UL));
- if (f >= kMaxInt) {
- return static_cast<int32_t>(0x7fffffffUL);
- } else if (f <= kMinInt) {
- return static_cast<int32_t>(0x80000000UL);
- } else if (f != f) { // NaN case
- return 0;
- } else {
- return static_cast<int32_t>(f);
- }
+ return art_float_to_integral<int32_t, float>(f);
}
} // namespace art
diff --git a/runtime/entrypoints/portable/portable_entrypoints.h b/runtime/entrypoints/portable/portable_entrypoints.h
index d4564471ac..dbea70735f 100644
--- a/runtime/entrypoints/portable/portable_entrypoints.h
+++ b/runtime/entrypoints/portable/portable_entrypoints.h
@@ -35,6 +35,7 @@ class Thread;
// compiler ABI.
struct PACKED(4) PortableEntryPoints {
// Invocation
+ void (*pPortableImtConflictTrampoline)(mirror::ArtMethod*);
void (*pPortableResolutionTrampoline)(mirror::ArtMethod*);
void (*pPortableToInterpreterBridge)(mirror::ArtMethod*);
};
diff --git a/runtime/entrypoints/portable/portable_field_entrypoints.cc b/runtime/entrypoints/portable/portable_field_entrypoints.cc
index bd6f79584b..095e99ef34 100644
--- a/runtime/entrypoints/portable/portable_field_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_field_entrypoints.cc
@@ -33,12 +33,8 @@ extern "C" int32_t art_portable_set32_static_from_code(uint32_t field_idx,
field->Set32(field->GetDeclaringClass(), new_value);
return 0;
}
- field = FindFieldFromCode(field_idx,
- referrer,
- Thread::Current(),
- StaticPrimitiveWrite,
- sizeof(uint32_t),
- true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
field->Set32(field->GetDeclaringClass(), new_value);
return 0;
@@ -55,12 +51,8 @@ extern "C" int32_t art_portable_set64_static_from_code(uint32_t field_idx,
field->Set64(field->GetDeclaringClass(), new_value);
return 0;
}
- field = FindFieldFromCode(field_idx,
- referrer,
- Thread::Current(),
- StaticPrimitiveWrite,
- sizeof(uint64_t),
- true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
field->Set64(field->GetDeclaringClass(), new_value);
return 0;
@@ -78,8 +70,8 @@ extern "C" int32_t art_portable_set_obj_static_from_code(uint32_t field_idx,
field->SetObj(field->GetDeclaringClass(), new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticObjectWrite, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
field->SetObj(field->GetDeclaringClass(), new_value);
return 0;
@@ -94,8 +86,8 @@ extern "C" int32_t art_portable_get32_static_from_code(uint32_t field_idx,
if (LIKELY(field != NULL)) {
return field->Get32(field->GetDeclaringClass());
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticPrimitiveRead, sizeof(uint32_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
return field->Get32(field->GetDeclaringClass());
}
@@ -109,8 +101,8 @@ extern "C" int64_t art_portable_get64_static_from_code(uint32_t field_idx,
if (LIKELY(field != NULL)) {
return field->Get64(field->GetDeclaringClass());
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticPrimitiveRead, sizeof(uint64_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
return field->Get64(field->GetDeclaringClass());
}
@@ -125,8 +117,8 @@ extern "C" mirror::Object* art_portable_get_obj_static_from_code(uint32_t field_
if (LIKELY(field != NULL)) {
return field->GetObj(field->GetDeclaringClass());
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- StaticObjectRead, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(field->GetDeclaringClass());
}
@@ -142,8 +134,8 @@ extern "C" int32_t art_portable_set32_instance_from_code(uint32_t field_idx,
field->Set32(obj, new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveWrite, sizeof(uint32_t), true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
field->Set32(obj, new_value);
return 0;
@@ -160,8 +152,8 @@ extern "C" int32_t art_portable_set64_instance_from_code(uint32_t field_idx,
field->Set64(obj, new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveWrite, sizeof(uint64_t), true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
field->Set64(obj, new_value);
return 0;
@@ -180,8 +172,8 @@ extern "C" int32_t art_portable_set_obj_instance_from_code(uint32_t field_idx,
field->SetObj(obj, new_value);
return 0;
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstanceObjectWrite, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
field->SetObj(obj, new_value);
return 0;
@@ -197,8 +189,8 @@ extern "C" int32_t art_portable_get32_instance_from_code(uint32_t field_idx,
if (LIKELY(field != NULL)) {
return field->Get32(obj);
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveRead, sizeof(uint32_t), true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint32_t));
if (LIKELY(field != NULL)) {
return field->Get32(obj);
}
@@ -213,8 +205,8 @@ extern "C" int64_t art_portable_get64_instance_from_code(uint32_t field_idx,
if (LIKELY(field != NULL)) {
return field->Get64(obj);
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstancePrimitiveRead, sizeof(uint64_t), true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(uint64_t));
if (LIKELY(field != NULL)) {
return field->Get64(obj);
}
@@ -230,8 +222,8 @@ extern "C" mirror::Object* art_portable_get_obj_instance_from_code(uint32_t fiel
if (LIKELY(field != NULL)) {
return field->GetObj(obj);
}
- field = FindFieldFromCode(field_idx, referrer, Thread::Current(),
- InstanceObjectRead, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, Thread::Current(),
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(obj);
}
diff --git a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
index 14cbd84186..e2a0cc2008 100644
--- a/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
+++ b/runtime/entrypoints/portable/portable_invoke_entrypoints.cc
@@ -21,21 +21,13 @@
namespace art {
-static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- bool access_check,
- InvokeType type,
- Thread* thread)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- mirror::ArtMethod* method = FindMethodFast(method_idx,
- this_object,
- caller_method,
- access_check,
- type);
+template<InvokeType type, bool access_check>
+static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* thread) {
+ mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method,
+ access_check, type);
if (UNLIKELY(method == NULL)) {
- method = FindMethodFromCode(method_idx, this_object, caller_method,
- thread, access_check, type);
+ method = FindMethodFromCode<type, access_check>(method_idx, this_object, caller_method, thread);
if (UNLIKELY(method == NULL)) {
CHECK(thread->IsExceptionPending());
return 0; // failure
@@ -53,12 +45,32 @@ static mirror::ArtMethod* FindMethodHelper(uint32_t method_idx,
return method;
}
+// Explicit template declarations of FindMethodHelper for all invoke types.
+#define EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ static mirror::ArtMethod* FindMethodHelper<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* caller_method, \
+ Thread* thread)
+#define EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_FIND_METHOD_HELPER_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_FIND_METHOD_HELPER_TEMPLATE_DECL
+
extern "C" mirror::Object* art_portable_find_static_method_from_code_with_access_check(uint32_t method_idx,
mirror::Object* this_object,
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kStatic, thread);
+ return FindMethodHelper<kStatic, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access_check(uint32_t method_idx,
@@ -66,7 +78,7 @@ extern "C" mirror::Object* art_portable_find_direct_method_from_code_with_access
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kDirect, thread);
+ return FindMethodHelper<kDirect, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_access_check(uint32_t method_idx,
@@ -74,7 +86,7 @@ extern "C" mirror::Object* art_portable_find_virtual_method_from_code_with_acces
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kVirtual, thread);
+ return FindMethodHelper<kVirtual, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_check(uint32_t method_idx,
@@ -82,7 +94,7 @@ extern "C" mirror::Object* art_portable_find_super_method_from_code_with_access_
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kSuper, thread);
+ return FindMethodHelper<kSuper, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_access_check(uint32_t method_idx,
@@ -90,7 +102,7 @@ extern "C" mirror::Object* art_portable_find_interface_method_from_code_with_acc
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, true, kInterface, thread);
+ return FindMethodHelper<kInterface, true>(method_idx, this_object, referrer, thread);
}
extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t method_idx,
@@ -98,7 +110,7 @@ extern "C" mirror::Object* art_portable_find_interface_method_from_code(uint32_t
mirror::ArtMethod* referrer,
Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FindMethodHelper(method_idx, this_object, referrer, false, kInterface, thread);
+ return FindMethodHelper<kInterface, false>(method_idx, this_object, referrer, thread);
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 420e63a1bb..6f7b1ab19b 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -76,4 +76,57 @@ extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck(uint32_t
return CheckAndAllocArrayFromCode(type_idx, method, component_count, self, true);
}
+extern "C" mirror::Object* artAllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocObjectFromCodeInstrumented(type_idx, method, self, false);
+}
+
+extern "C" mirror::Object* artAllocObjectFromCodeWithAccessCheckInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocObjectFromCodeInstrumented(type_idx, method, self, true);
+}
+
+extern "C" mirror::Array* artAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method,
+ int32_t component_count, Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false);
+}
+
+extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return AllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true);
+}
+
+extern "C" mirror::Array* artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count, Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, false);
+}
+
+extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(uint32_t type_idx,
+ mirror::ArtMethod* method,
+ int32_t component_count,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return CheckAndAllocArrayFromCodeInstrumented(type_idx, method, component_count, self, true);
+}
+
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_cast_entrypoints.cc b/runtime/entrypoints/quick/quick_cast_entrypoints.cc
index 9ffa736614..ae53d6c7be 100644
--- a/runtime/entrypoints/quick/quick_cast_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_cast_entrypoints.cc
@@ -14,11 +14,8 @@
* limitations under the License.
*/
-#include "callee_save_frame.h"
-#include "entrypoints/entrypoint_utils.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
namespace art {
@@ -31,38 +28,4 @@ extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass,
return klass->IsAssignableFrom(ref_class) ? 1 : 0;
}
-// Check whether it is safe to cast one class to the other, throw exception and return -1 on failure
-extern "C" int artCheckCastFromCode(mirror::Class* src_type, mirror::Class* dest_type,
- Thread* self, mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(src_type->IsClass()) << PrettyClass(src_type);
- DCHECK(dest_type->IsClass()) << PrettyClass(dest_type);
- if (LIKELY(dest_type->IsAssignableFrom(src_type))) {
- return 0; // Success
- } else {
- FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- ThrowClassCastException(dest_type, src_type);
- return -1; // Failure
- }
-}
-
-// Tests whether 'element' can be assigned into an array of type 'array_class'.
-// Returns 0 on success and -1 if an exception is pending.
-extern "C" int artCanPutArrayElementFromCode(const mirror::Object* element,
- const mirror::Class* array_class,
- Thread* self, mirror::ArtMethod** sp)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(array_class != NULL);
- // element can't be NULL as we catch this is screened in runtime_support
- mirror::Class* element_class = element->GetClass();
- mirror::Class* component_type = array_class->GetComponentType();
- if (LIKELY(component_type->IsAssignableFrom(element_class))) {
- return 0; // Success
- } else {
- FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- ThrowArrayStoreException(element_class, array_class);
- return -1; // Failure
- }
-}
-
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 9d3b8ef27c..1ba206629e 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -48,7 +48,6 @@ struct PACKED(4) QuickEntryPoints {
// Cast
uint32_t (*pInstanceofNonTrivial)(const mirror::Class*, const mirror::Class*);
- void (*pCanPutArrayElement)(void*, void*);
void (*pCheckCast)(void*, void*);
// DexCache
@@ -71,7 +70,10 @@ struct PACKED(4) QuickEntryPoints {
void* (*pGetObjInstance)(uint32_t, void*);
void* (*pGetObjStatic)(uint32_t);
- // FillArray
+ // Array
+ void (*pAputObjectWithNullAndBoundCheck)(void*, uint32_t, void*); // array, index, src
+ void (*pAputObjectWithBoundCheck)(void*, uint32_t, void*); // array, index, src
+ void (*pAputObject)(void*, uint32_t, void*); // array, index, src
void (*pHandleFillArrayData)(void*, void*);
// JNI
@@ -103,7 +105,7 @@ struct PACKED(4) QuickEntryPoints {
int64_t (*pD2l)(double);
int64_t (*pF2l)(float);
int64_t (*pLdiv)(int64_t, int64_t);
- int64_t (*pLdivmod)(int64_t, int64_t);
+ int64_t (*pLmod)(int64_t, int64_t);
int64_t (*pLmul)(int64_t, int64_t);
uint64_t (*pShlLong)(uint64_t, uint32_t);
uint64_t (*pShrLong)(uint64_t, uint32_t);
@@ -116,10 +118,10 @@ struct PACKED(4) QuickEntryPoints {
void* (*pMemcpy)(void*, const void*, size_t);
// Invocation
+ void (*pQuickImtConflictTrampoline)(mirror::ArtMethod*);
void (*pQuickResolutionTrampoline)(mirror::ArtMethod*);
void (*pQuickToInterpreterBridge)(mirror::ArtMethod*);
void (*pInvokeDirectTrampolineWithAccessCheck)(uint32_t, void*);
- void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
void (*pInvokeInterfaceTrampolineWithAccessCheck)(uint32_t, void*);
void (*pInvokeStaticTrampolineWithAccessCheck)(uint32_t, void*);
void (*pInvokeSuperTrampolineWithAccessCheck)(uint32_t, void*);
@@ -140,22 +142,23 @@ struct PACKED(4) QuickEntryPoints {
// JNI entrypoints.
-extern uint32_t JniMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
+extern uint32_t JniMethodStart(Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self)
- UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie,
Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result,
uint32_t saved_local_ref_cookie,
jobject locked, Thread* self)
- SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+ NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 0ec1eb7920..0a533bdfbc 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -35,7 +35,7 @@ extern "C" uint32_t artGet32StaticFromCode(uint32_t field_idx,
return field->Get32(field->GetDeclaringClass());
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int32_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int32_t));
if (LIKELY(field != NULL)) {
return field->Get32(field->GetDeclaringClass());
}
@@ -52,7 +52,7 @@ extern "C" uint64_t artGet64StaticFromCode(uint32_t field_idx,
return field->Get64(field->GetDeclaringClass());
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveRead, sizeof(int64_t), true);
+ field = FindFieldFromCode<StaticPrimitiveRead, true>(field_idx, referrer, self, sizeof(int64_t));
if (LIKELY(field != NULL)) {
return field->Get64(field->GetDeclaringClass());
}
@@ -69,8 +69,8 @@ extern "C" mirror::Object* artGetObjStaticFromCode(uint32_t field_idx,
return field->GetObj(field->GetDeclaringClass());
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticObjectRead, sizeof(mirror::Object*),
- true);
+ field = FindFieldFromCode<StaticObjectRead, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
return field->GetObj(field->GetDeclaringClass());
}
@@ -87,8 +87,8 @@ extern "C" uint32_t artGet32InstanceFromCode(uint32_t field_idx, mirror::Object*
return field->Get32(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int32_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -110,8 +110,8 @@ extern "C" uint64_t artGet64InstanceFromCode(uint32_t field_idx, mirror::Object*
return field->Get64(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveRead, sizeof(int64_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveRead, true>(field_idx, referrer, self,
+ sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -134,8 +134,8 @@ extern "C" mirror::Object* artGetObjInstanceFromCode(uint32_t field_idx, mirror:
return field->GetObj(obj);
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectRead, sizeof(mirror::Object*),
- true);
+ field = FindFieldFromCode<InstanceObjectRead, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -158,7 +158,7 @@ extern "C" int artSet32StaticFromCode(uint32_t field_idx, uint32_t new_value,
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int32_t), true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int32_t));
if (LIKELY(field != NULL)) {
field->Set32(field->GetDeclaringClass(), new_value);
return 0; // success
@@ -176,7 +176,7 @@ extern "C" int artSet64StaticFromCode(uint32_t field_idx, const mirror::ArtMetho
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticPrimitiveWrite, sizeof(int64_t), true);
+ field = FindFieldFromCode<StaticPrimitiveWrite, true>(field_idx, referrer, self, sizeof(int64_t));
if (LIKELY(field != NULL)) {
field->Set64(field->GetDeclaringClass(), new_value);
return 0; // success
@@ -197,7 +197,8 @@ extern "C" int artSetObjStaticFromCode(uint32_t field_idx, mirror::Object* new_v
}
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, StaticObjectWrite, sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<StaticObjectWrite, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
field->SetObj(field->GetDeclaringClass(), new_value);
return 0; // success
@@ -216,8 +217,8 @@ extern "C" int artSet32InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int32_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+ sizeof(int32_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -244,8 +245,8 @@ extern "C" int artSet64InstanceFromCode(uint32_t field_idx, mirror::Object* obj,
}
*sp = callee_save;
self->SetTopOfStack(sp, 0);
- field = FindFieldFromCode(field_idx, referrer, self, InstancePrimitiveWrite, sizeof(int64_t),
- true);
+ field = FindFieldFromCode<InstancePrimitiveWrite, true>(field_idx, referrer, self,
+ sizeof(int64_t));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
@@ -270,8 +271,8 @@ extern "C" int artSetObjInstanceFromCode(uint32_t field_idx, mirror::Object* obj
return 0; // success
}
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- field = FindFieldFromCode(field_idx, referrer, self, InstanceObjectWrite,
- sizeof(mirror::Object*), true);
+ field = FindFieldFromCode<InstanceObjectWrite, true>(field_idx, referrer, self,
+ sizeof(mirror::Object*));
if (LIKELY(field != NULL)) {
if (UNLIKELY(obj == NULL)) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
diff --git a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc
index 07c1c015aa..b852a3292e 100644
--- a/runtime/entrypoints/quick/quick_invoke_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_invoke_entrypoints.cc
@@ -118,8 +118,7 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me
DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
dex_method_idx = instr->VRegB_3rc();
}
- method = FindMethodFromCode(dex_method_idx, this_object, caller_method, self,
- false, kInterface);
+ method = FindMethodFromCode<kInterface, false>(dex_method_idx, this_object, caller_method, self);
if (UNLIKELY(method == NULL)) {
CHECK(self->IsExceptionPending());
return 0; // Failure.
@@ -142,17 +141,15 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_me
return result;
}
-
+template<InvokeType type, bool access_check>
static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
mirror::ArtMethod* caller_method,
- Thread* self, mirror::ArtMethod** sp, bool access_check,
- InvokeType type)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Thread* self, mirror::ArtMethod** sp) {
mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method,
access_check, type);
if (UNLIKELY(method == NULL)) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
- method = FindMethodFromCode(method_idx, this_object, caller_method, self, access_check, type);
+ method = FindMethodFromCode<type, access_check>(method_idx, this_object, caller_method, self);
if (UNLIKELY(method == NULL)) {
CHECK(self->IsExceptionPending());
return 0; // failure
@@ -176,6 +173,27 @@ static uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object
return result;
}
+// Explicit template declarations of artInvokeCommon for all invoke types.
+#define EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, _access_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ static uint64_t artInvokeCommon<_type, _access_check>(uint32_t method_idx, \
+ mirror::Object* this_object, \
+ mirror::ArtMethod* caller_method, \
+ Thread* self, mirror::ArtMethod** sp)
+
+#define EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(_type) \
+ EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, false); \
+ EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL(_type, true)
+
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kStatic);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kDirect);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kVirtual);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kSuper);
+EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL(kInterface);
+
+#undef EXPLICIT_ART_INVOKE_COMMON_TYPED_TEMPLATE_DECL
+#undef EXPLICIT_ART_INVOKE_COMMON_TEMPLATE_DECL
+
// See comments in runtime_support_asm.S
extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
mirror::Object* this_object,
@@ -183,7 +201,7 @@ extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kInterface);
+ return artInvokeCommon<kInterface, true>(method_idx, this_object, caller_method, self, sp);
}
@@ -193,7 +211,7 @@ extern "C" uint64_t artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kDirect);
+ return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method, self, sp);
}
extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
@@ -202,7 +220,7 @@ extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kStatic);
+ return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method, self, sp);
}
extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
@@ -211,7 +229,7 @@ extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kSuper);
+ return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method, self, sp);
}
extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
@@ -220,7 +238,7 @@ extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_id
Thread* self,
mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon(method_idx, this_object, caller_method, self, sp, true, kVirtual);
+ return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method, self, sp);
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 27ae59b9b0..59da7a05cb 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -30,10 +30,14 @@ namespace art {
// Called on entry to JNI, transition out of Runnable and release share of mutator_lock_.
extern uint32_t JniMethodStart(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
- DCHECK(env != NULL);
+ DCHECK(env != nullptr);
uint32_t saved_local_ref_cookie = env->local_ref_cookie;
env->local_ref_cookie = env->locals.GetSegmentState();
- self->TransitionFromRunnableToSuspended(kNative);
+ mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+ if (!native_method->IsFastNative()) {
+ // When not fast JNI we transition out of runnable.
+ self->TransitionFromRunnableToSuspended(kNative);
+ }
return saved_local_ref_cookie;
}
@@ -42,6 +46,20 @@ extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self) {
return JniMethodStart(self);
}
+// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
+static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+ mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+ bool is_fast = native_method->IsFastNative();
+ if (!is_fast) {
+ self->TransitionFromSuspendedToRunnable();
+ } else if (UNLIKELY(self->TestAllFlags())) {
+ // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there
+ // is a flag raised.
+ DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
+ CheckSuspend(self);
+ }
+}
+
static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
env->locals.SetSegmentState(env->local_ref_cookie);
@@ -50,21 +68,21 @@ static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) {
}
extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
PopLocalReferences(saved_local_ref_cookie, self);
}
extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
UnlockJniSynchronizedMethod(locked, self); // Must decode before pop.
PopLocalReferences(saved_local_ref_cookie, self);
}
extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie,
Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
mirror::Object* o = self->DecodeJObject(result); // Must decode before pop.
PopLocalReferences(saved_local_ref_cookie, self);
// Process result.
@@ -80,7 +98,7 @@ extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_
extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result,
uint32_t saved_local_ref_cookie,
jobject locked, Thread* self) {
- self->TransitionFromSuspendedToRunnable();
+ GoToRunnable(self);
UnlockJniSynchronizedMethod(locked, self); // Must decode before pop.
mirror::Object* o = self->DecodeJObject(result);
PopLocalReferences(saved_local_ref_cookie, self);
diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
index 36ca6044a2..2102ab1bea 100644
--- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
@@ -15,28 +15,40 @@
*/
#include "callee_save_frame.h"
+#include "common_throws.h"
#include "mirror/object-inl.h"
namespace art {
-extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self,
- mirror::ArtMethod** sp)
- UNLOCK_FUNCTION(monitor_lock_) {
+extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, mirror::ArtMethod** sp)
+ EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) {
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
- DCHECK(obj != NULL); // Assumed to have been checked before entry
- // MonitorExit may throw exception
- return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */;
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location(self->GetCurrentLocationForThrow());
+ ThrowNullPointerException(&throw_location,
+ "Null reference used for synchronization (monitor-enter)");
+ return -1; // Failure.
+ } else {
+ obj->MonitorEnter(self); // May block
+ DCHECK(self->HoldsLock(obj));
+ DCHECK(!self->IsExceptionPending());
+ return 0; // Success.
+ // Only possible exception is NPE and is handled before entry
+ }
}
-extern "C" void artLockObjectFromCode(mirror::Object* obj, Thread* thread,
- mirror::ArtMethod** sp)
- EXCLUSIVE_LOCK_FUNCTION(monitor_lock_) {
- FinishCalleeSaveFrameSetup(thread, sp, Runtime::kRefsOnly);
- DCHECK(obj != NULL); // Assumed to have been checked before entry
- obj->MonitorEnter(thread); // May block
- DCHECK(thread->HoldsLock(obj));
- // Only possible exception is NPE and is handled before entry
- DCHECK(!thread->IsExceptionPending());
+extern "C" int artUnlockObjectFromCode(mirror::Object* obj, Thread* self, mirror::ArtMethod** sp)
+ UNLOCK_FUNCTION(monitor_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ if (UNLIKELY(obj == NULL)) {
+ ThrowLocation throw_location(self->GetCurrentLocationForThrow());
+ ThrowNullPointerException(&throw_location,
+ "Null reference used for synchronization (monitor-exit)");
+ return -1; // Failure.
+ } else {
+ // MonitorExit may throw exception.
+ return obj->MonitorExit(self) ? 0 /* Success */ : -1 /* Failure */;
+ }
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_math_entrypoints.cc b/runtime/entrypoints/quick/quick_math_entrypoints.cc
index 0bfe59dc2f..014aad3b90 100644
--- a/runtime/entrypoints/quick/quick_math_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_math_entrypoints.cc
@@ -62,15 +62,15 @@ int CmplDouble(double a, double b) {
return -1;
}
-extern "C" int64_t artLmulFromCode(int64_t a, int64_t b) {
+extern "C" int64_t artLmul(int64_t a, int64_t b) {
return a * b;
}
-extern "C" int64_t artLdivFromCode(int64_t a, int64_t b) {
+extern "C" int64_t artLdiv(int64_t a, int64_t b) {
return a / b;
}
-extern "C" int64_t artLdivmodFromCode(int64_t a, int64_t b) {
+extern "C" int64_t artLmod(int64_t a, int64_t b) {
return a % b;
}
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index f67b2fc4ab..31eacac114 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -15,8 +15,9 @@
*/
#include "callee_save_frame.h"
+#include "common_throws.h"
#include "entrypoints/entrypoint_utils.h"
-#include "mirror/object.h"
+#include "mirror/object-inl.h"
#include "object_utils.h"
#include "thread.h"
#include "well_known_classes.h"
@@ -95,4 +96,21 @@ extern "C" void artThrowNoSuchMethodFromCode(int32_t method_idx, Thread* self,
self->QuickDeliverException();
}
+extern "C" void artThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+ CHECK(!dest_type->IsAssignableFrom(src_type));
+ ThrowClassCastException(dest_type, src_type);
+ self->QuickDeliverException();
+}
+
+extern "C" void artThrowArrayStoreException(mirror::Object* array, mirror::Object* value,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll);
+ ThrowArrayStoreException(value->GetClass(), array->GetClass());
+ self->QuickDeliverException();
+}
+
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index cb486d5827..01d3549985 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -19,6 +19,7 @@
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
#include "entrypoints/entrypoint_utils.h"
+#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
#include "invoke_arg_array_builder.h"
#include "mirror/art_method-inl.h"
@@ -423,13 +424,23 @@ class RememberFoGcArgumentVisitor : public QuickArgumentVisitor {
virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (IsParamAReference()) {
- soa_->AddLocalReference<jobject>(*reinterpret_cast<mirror::Object**>(GetParamAddress()));
+ mirror::Object** param_address = reinterpret_cast<mirror::Object**>(GetParamAddress());
+ jobject reference =
+ soa_->AddLocalReference<jobject>(*param_address);
+ references_.push_back(std::make_pair(reference, param_address));
+ }
+ }
+
+ void FixupReferences() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Fixup any references which may have changed.
+ for (std::pair<jobject, mirror::Object**>& it : references_) {
+ *it.second = soa_->Decode<mirror::Object*>(it.first);
}
}
private:
ScopedObjectAccessUnchecked* soa_;
-
+ std::vector<std::pair<jobject, mirror::Object**> > references_;
DISALLOW_COPY_AND_ASSIGN(RememberFoGcArgumentVisitor);
};
@@ -537,6 +548,21 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
} else if (invoke_type == kInterface) {
called = receiver->GetClass()->FindVirtualMethodForInterface(called);
}
+ if ((invoke_type == kVirtual) || (invoke_type == kInterface)) {
+ // We came here because of sharpening. Ensure the dex cache is up-to-date on the method index
+ // of the sharpened method.
+ if (called->GetDexCacheResolvedMethods() == caller->GetDexCacheResolvedMethods()) {
+ caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called);
+ } else {
+ // Calling from one dex file to another, need to compute the method index appropriate to
+ // the caller's dex file.
+ uint32_t method_index =
+ MethodHelper(called).FindDexMethodIndexInOtherDexFile(MethodHelper(caller).GetDexFile());
+ if (method_index != DexFile::kDexNoIndex) {
+ caller->GetDexCacheResolvedMethods()->Set(method_index, called);
+ }
+ }
+ }
// Ensure that the called method's class is initialized.
mirror::Class* called_class = called->GetDeclaringClass();
linker->EnsureInitialized(called_class, true, true);
@@ -556,11 +582,8 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
}
}
CHECK_EQ(code == NULL, thread->IsExceptionPending());
-#ifdef MOVING_GARBAGE_COLLECTOR
- // TODO: locally saved objects may have moved during a GC during resolution. Need to update the
- // registers so that the stale objects aren't passed to the method we've resolved.
- UNIMPLEMENTED(WARNING);
-#endif
+ // Fixup any locally saved objects may have moved during a GC.
+ visitor.FixupReferences();
// Place called method in callee-save frame to be placed as first argument to quick method.
*sp = called;
return code;
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index 2e6b0a8f56..a5f999784d 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -146,7 +146,7 @@ TEST_F(ExceptionTest, StackTraceElement) {
ScopedObjectAccess soa(env);
std::vector<uintptr_t> fake_stack;
- ASSERT_EQ(kStackAlignment, 16);
+ ASSERT_EQ(kStackAlignment, 16U);
ASSERT_EQ(sizeof(uintptr_t), sizeof(uint32_t));
#if !defined(ART_USE_PORTABLE_COMPILER)
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 997d7251fe..8fa5b86f9a 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -163,8 +163,10 @@ class AtomicStack {
// Size in number of elements.
void Init() {
- mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T), PROT_READ | PROT_WRITE));
- CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack";
+ std::string error_msg;
+ mem_map_.reset(MemMap::MapAnonymous(name_.c_str(), NULL, capacity_ * sizeof(T),
+ PROT_READ | PROT_WRITE, &error_msg));
+ CHECK(mem_map_.get() != NULL) << "couldn't allocate mark stack.\n" << error_msg;
byte* addr = mem_map_->Begin();
CHECK(addr != NULL);
debug_is_sorted_ = true;
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index 85034a0f27..7818bc8ba6 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -54,9 +54,11 @@ CardTable* CardTable::Create(const byte* heap_begin, size_t heap_capacity) {
/* Set up the card table */
size_t capacity = heap_capacity / kCardSize;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
+ std::string error_msg;
UniquePtr<MemMap> mem_map(MemMap::MapAnonymous("card table", NULL,
- capacity + 256, PROT_READ | PROT_WRITE));
- CHECK(mem_map.get() != NULL) << "couldn't allocate card table";
+ capacity + 256, PROT_READ | PROT_WRITE,
+ &error_msg));
+ CHECK(mem_map.get() != NULL) << "couldn't allocate card table: " << error_msg;
// All zeros is the correct initial value; all clean. Anonymous mmaps are initialized to zero, we
// don't clear the card table to avoid unnecessary pages being allocated
COMPILE_ASSERT(kCardClean == 0, card_clean_must_be_0);
diff --git a/runtime/gc/accounting/gc_allocator.cc b/runtime/gc/accounting/gc_allocator.cc
index 11d0e679b1..49d84faf3c 100644
--- a/runtime/gc/accounting/gc_allocator.cc
+++ b/runtime/gc/accounting/gc_allocator.cc
@@ -22,15 +22,17 @@
namespace art {
namespace gc {
namespace accounting {
- void* RegisterGCAllocation(size_t bytes) {
- Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes);
- return malloc(bytes);
- }
- void RegisterGCDeAllocation(void* p, size_t bytes) {
- Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes);
- free(p);
- }
+void* RegisterGcAllocation(size_t bytes) {
+ Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes);
+ return malloc(bytes);
+}
+
+void RegisterGcDeallocation(void* p, size_t bytes) {
+ Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes);
+ free(p);
+}
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/gc_allocator.h b/runtime/gc/accounting/gc_allocator.h
index 1fba858882..4fe936735f 100644
--- a/runtime/gc/accounting/gc_allocator.h
+++ b/runtime/gc/accounting/gc_allocator.h
@@ -26,55 +26,56 @@
namespace art {
namespace gc {
namespace accounting {
- void* RegisterGCAllocation(size_t bytes);
- void RegisterGCDeAllocation(void* p, size_t bytes);
- static const bool kMeasureGCMemoryOverhead = false;
+void* RegisterGcAllocation(size_t bytes);
+void RegisterGcDeallocation(void* p, size_t bytes);
- template <typename T>
- class GCAllocatorImpl : public std::allocator<T> {
- public:
- typedef typename std::allocator<T>::value_type value_type;
- typedef typename std::allocator<T>::size_type size_type;
- typedef typename std::allocator<T>::difference_type difference_type;
- typedef typename std::allocator<T>::pointer pointer;
- typedef typename std::allocator<T>::const_pointer const_pointer;
- typedef typename std::allocator<T>::reference reference;
- typedef typename std::allocator<T>::const_reference const_reference;
+static const bool kMeasureGcMemoryOverhead = false;
- // Used internally by STL data structures.
- template <class U>
- GCAllocatorImpl(const GCAllocatorImpl<U>& alloc) throw() {
- }
+template <typename T>
+class GcAllocatorImpl : public std::allocator<T> {
+ public:
+ typedef typename std::allocator<T>::value_type value_type;
+ typedef typename std::allocator<T>::size_type size_type;
+ typedef typename std::allocator<T>::difference_type difference_type;
+ typedef typename std::allocator<T>::pointer pointer;
+ typedef typename std::allocator<T>::const_pointer const_pointer;
+ typedef typename std::allocator<T>::reference reference;
+ typedef typename std::allocator<T>::const_reference const_reference;
- // Used internally by STL data structures.
- GCAllocatorImpl() throw() {
- }
+ // Used internally by STL data structures.
+ template <class U>
+ GcAllocatorImpl(const GcAllocatorImpl<U>& alloc) throw() {
+ }
- // Enables an allocator for objects of one type to allocate storage for objects of another type.
- // Used internally by STL data structures.
- template <class U>
- struct rebind {
- typedef GCAllocatorImpl<U> other;
- };
+ // Used internally by STL data structures.
+ GcAllocatorImpl() throw() {
+ }
- pointer allocate(size_type n, const_pointer hint = 0) {
- return reinterpret_cast<pointer>(RegisterGCAllocation(n * sizeof(T)));
- }
-
- template <typename PT>
- void deallocate(PT p, size_type n) {
- RegisterGCDeAllocation(p, n * sizeof(T));
- }
+ // Enables an allocator for objects of one type to allocate storage for objects of another type.
+ // Used internally by STL data structures.
+ template <class U>
+ struct rebind {
+ typedef GcAllocatorImpl<U> other;
};
- // C++ doesn't allow template typedefs. This is a workaround template typedef which is
- // GCAllocatorImpl<T> if kMeasureGCMemoryOverhead is true, std::allocator<T> otherwise.
- template <typename T>
- class GCAllocator : public TypeStaticIf<kMeasureGCMemoryOverhead,
- GCAllocatorImpl<T>,
- std::allocator<T> >::value {
- };
+ pointer allocate(size_type n, const_pointer hint = 0) {
+ return reinterpret_cast<pointer>(RegisterGcAllocation(n * sizeof(T)));
+ }
+
+ template <typename PT>
+ void deallocate(PT p, size_type n) {
+ RegisterGcDeallocation(p, n * sizeof(T));
+ }
+};
+
+// C++ doesn't allow template typedefs. This is a workaround template typedef which is
+// GCAllocatorImpl<T> if kMeasureGCMemoryOverhead is true, std::allocator<T> otherwise.
+template <typename T>
+class GcAllocator : public TypeStaticIf<kMeasureGcMemoryOverhead, GcAllocatorImpl<T>,
+ std::allocator<T> >::value {
+};
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h
index 2ca8c4a82e..24ebbaa1b5 100644
--- a/runtime/gc/accounting/heap_bitmap.h
+++ b/runtime/gc/accounting/heap_bitmap.h
@@ -31,8 +31,8 @@ namespace accounting {
class HeapBitmap {
public:
- typedef std::vector<SpaceBitmap*, GCAllocator<SpaceBitmap*> > SpaceBitmapVector;
- typedef std::vector<SpaceSetMap*, GCAllocator<SpaceSetMap*> > SpaceSetMapVector;
+ typedef std::vector<SpaceBitmap*, GcAllocator<SpaceBitmap*> > SpaceBitmapVector;
+ typedef std::vector<SpaceSetMap*, GcAllocator<SpaceSetMap*> > SpaceSetMapVector;
bool Test(const mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) {
SpaceBitmap* bitmap = GetContinuousSpaceBitmap(obj);
diff --git a/runtime/gc/accounting/mod_union_table-inl.h b/runtime/gc/accounting/mod_union_table-inl.h
index 29450c1d34..fb425df78a 100644
--- a/runtime/gc/accounting/mod_union_table-inl.h
+++ b/runtime/gc/accounting/mod_union_table-inl.h
@@ -28,9 +28,11 @@ namespace accounting {
// A mod-union table to record image references to the Zygote and alloc space.
class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache {
public:
- explicit ModUnionTableToZygoteAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) {}
+ explicit ModUnionTableToZygoteAllocspace(const std::string& name, Heap* heap,
+ space::ContinuousSpace* space)
+ : ModUnionTableReferenceCache(name, heap, space) {}
- bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) {
+ bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) ALWAYS_INLINE {
const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces();
typedef std::vector<space::ContinuousSpace*>::const_iterator It;
for (It it = spaces.begin(); it != spaces.end(); ++it) {
@@ -47,16 +49,18 @@ class ModUnionTableToZygoteAllocspace : public ModUnionTableReferenceCache {
// A mod-union table to record Zygote references to the alloc space.
class ModUnionTableToAllocspace : public ModUnionTableReferenceCache {
public:
- explicit ModUnionTableToAllocspace(Heap* heap) : ModUnionTableReferenceCache(heap) {}
+ explicit ModUnionTableToAllocspace(const std::string& name, Heap* heap,
+ space::ContinuousSpace* space)
+ : ModUnionTableReferenceCache(name, heap, space) {}
- bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) {
+ bool AddReference(const mirror::Object* /* obj */, const mirror::Object* ref) ALWAYS_INLINE {
const std::vector<space::ContinuousSpace*>& spaces = GetHeap()->GetContinuousSpaces();
typedef std::vector<space::ContinuousSpace*>::const_iterator It;
for (It it = spaces.begin(); it != spaces.end(); ++it) {
space::ContinuousSpace* space = *it;
if (space->Contains(ref)) {
// The allocation space is always considered for collection whereas the Zygote space is
- //
+ // only considered for full GC.
return space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect;
}
}
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 486521951a..7cbe94d3d2 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -19,6 +19,7 @@
#include "base/stl_util.h"
#include "card_table-inl.h"
#include "heap_bitmap.h"
+#include "gc/collector/mark_sweep.h"
#include "gc/collector/mark_sweep-inl.h"
#include "gc/heap.h"
#include "gc/space/space.h"
@@ -67,60 +68,87 @@ class ModUnionClearCardVisitor {
std::vector<byte*>* const cleared_cards_;
};
+class ModUnionUpdateObjectReferencesVisitor {
+ public:
+ ModUnionUpdateObjectReferencesVisitor(RootVisitor visitor, void* arg)
+ : visitor_(visitor),
+ arg_(arg) {
+ }
+
+ // Extra parameters are required since we use this same visitor signature for checking objects.
+ void operator()(Object* obj, Object* ref, const MemberOffset& offset,
+ bool /* is_static */) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Only add the reference if it is non null and fits our criteria.
+ if (ref != nullptr) {
+ Object* new_ref = visitor_(ref, arg_);
+ if (new_ref != ref) {
+ obj->SetFieldObject(offset, ref, false, true);
+ }
+ }
+ }
+
+ private:
+ RootVisitor* visitor_;
+ void* arg_;
+};
+
class ModUnionScanImageRootVisitor {
public:
- explicit ModUnionScanImageRootVisitor(collector::MarkSweep* const mark_sweep)
- : mark_sweep_(mark_sweep) {}
+ ModUnionScanImageRootVisitor(RootVisitor visitor, void* arg)
+ : visitor_(visitor), arg_(arg) {}
- void operator()(const Object* root) const
+ void operator()(Object* root) const
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(root != NULL);
- mark_sweep_->ScanRoot(root);
+ ModUnionUpdateObjectReferencesVisitor ref_visitor(visitor_, arg_);
+ collector::MarkSweep::VisitObjectReferences(root, ref_visitor, true);
}
private:
- collector::MarkSweep* const mark_sweep_;
+ RootVisitor* visitor_;
+ void* arg_;
};
-void ModUnionTableReferenceCache::ClearCards(space::ContinuousSpace* space) {
+void ModUnionTableReferenceCache::ClearCards() {
CardTable* card_table = GetHeap()->GetCardTable();
ModUnionClearCardSetVisitor visitor(&cleared_cards_);
// Clear dirty cards in the this space and update the corresponding mod-union bits.
- card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor);
+ card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
}
class AddToReferenceArrayVisitor {
public:
explicit AddToReferenceArrayVisitor(ModUnionTableReferenceCache* mod_union_table,
- std::vector<const Object*>* references)
+ std::vector<Object**>* references)
: mod_union_table_(mod_union_table),
references_(references) {
}
// Extra parameters are required since we use this same visitor signature for checking objects.
- void operator()(const Object* obj, const Object* ref, const MemberOffset& /* offset */,
+ void operator()(Object* obj, Object* ref, const MemberOffset& offset,
bool /* is_static */) const {
// Only add the reference if it is non null and fits our criteria.
- if (ref != NULL && mod_union_table_->AddReference(obj, ref)) {
- references_->push_back(ref);
+ if (ref != nullptr && mod_union_table_->AddReference(obj, ref)) {
+ // Push the adddress of the reference.
+ references_->push_back(obj->GetFieldObjectAddr(offset));
}
}
private:
ModUnionTableReferenceCache* const mod_union_table_;
- std::vector<const Object*>* const references_;
+ std::vector<Object**>* const references_;
};
class ModUnionReferenceVisitor {
public:
explicit ModUnionReferenceVisitor(ModUnionTableReferenceCache* const mod_union_table,
- std::vector<const Object*>* references)
+ std::vector<Object**>* references)
: mod_union_table_(mod_union_table),
references_(references) {
}
- void operator()(const Object* obj) const
+ void operator()(Object* obj) const
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
DCHECK(obj != NULL);
// We don't have an early exit since we use the visitor pattern, an early
@@ -130,7 +158,7 @@ class ModUnionReferenceVisitor {
}
private:
ModUnionTableReferenceCache* const mod_union_table_;
- std::vector<const Object*>* const references_;
+ std::vector<Object**>* const references_;
};
class CheckReferenceVisitor {
@@ -143,8 +171,8 @@ class CheckReferenceVisitor {
// Extra parameters are required since we use this same visitor signature for checking objects.
// TODO: Fixme when anotatalysis works with visitors.
- void operator()(const Object* obj, const Object* ref, const MemberOffset& /* offset */,
- bool /* is_static */) const
+ void operator()(const Object* obj, const Object* ref,
+ const MemberOffset& /* offset */, bool /* is_static */) const
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
Heap* heap = mod_union_table_->GetHeap();
if (ref != NULL && mod_union_table_->AddReference(obj, ref) &&
@@ -174,7 +202,7 @@ class ModUnionCheckReferences {
: mod_union_table_(mod_union_table), references_(references) {
}
- void operator()(const Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
+ void operator()(Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current());
DCHECK(obj != NULL);
CheckReferenceVisitor visitor(mod_union_table_, references_);
@@ -188,26 +216,25 @@ class ModUnionCheckReferences {
void ModUnionTableReferenceCache::Verify() {
// Start by checking that everything in the mod union table is marked.
- Heap* heap = GetHeap();
- for (const std::pair<const byte*, std::vector<const Object*> >& it : references_) {
- for (const Object* ref : it.second) {
- CHECK(heap->IsLiveObjectLocked(ref));
+ for (const auto& ref_pair : references_) {
+ for (Object** ref : ref_pair.second) {
+ CHECK(heap_->IsLiveObjectLocked(*ref));
}
}
// Check the references of each clean card which is also in the mod union table.
- CardTable* card_table = heap->GetCardTable();
- for (const std::pair<const byte*, std::vector<const Object*> > & it : references_) {
- const byte* card = it.first;
+ CardTable* card_table = heap_->GetCardTable();
+ SpaceBitmap* live_bitmap = space_->GetLiveBitmap();
+ for (const auto& ref_pair : references_) {
+ const byte* card = ref_pair.first;
if (*card == CardTable::kCardClean) {
- std::set<const Object*> reference_set(it.second.begin(), it.second.end());
+ std::set<const Object*> reference_set;
+ for (Object** obj_ptr : ref_pair.second) {
+ reference_set.insert(*obj_ptr);
+ }
ModUnionCheckReferences visitor(this, reference_set);
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
- uintptr_t end = start + CardTable::kCardSize;
- auto* space = heap->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false);
- DCHECK(space != nullptr);
- SpaceBitmap* live_bitmap = space->GetLiveBitmap();
- live_bitmap->VisitMarkedRange(start, end, visitor);
+ live_bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, visitor);
}
}
}
@@ -221,24 +248,24 @@ void ModUnionTableReferenceCache::Dump(std::ostream& os) {
os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ",";
}
os << "]\nModUnionTable references: [";
- for (const std::pair<const byte*, std::vector<const Object*> >& it : references_) {
- const byte* card_addr = it.first;
+ for (const auto& ref_pair : references_) {
+ const byte* card_addr = ref_pair.first;
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
uintptr_t end = start + CardTable::kCardSize;
os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "->{";
- for (const mirror::Object* ref : it.second) {
- os << reinterpret_cast<const void*>(ref) << ",";
+ for (Object** ref : ref_pair.second) {
+ os << reinterpret_cast<const void*>(*ref) << ",";
}
os << "},";
}
}
-void ModUnionTableReferenceCache::Update() {
+void ModUnionTableReferenceCache::UpdateAndMarkReferences(RootVisitor visitor, void* arg) {
Heap* heap = GetHeap();
CardTable* card_table = heap->GetCardTable();
- std::vector<const Object*> cards_references;
- ModUnionReferenceVisitor visitor(this, &cards_references);
+ std::vector<Object**> cards_references;
+ ModUnionReferenceVisitor add_visitor(this, &cards_references);
for (const auto& card : cleared_cards_) {
// Clear and re-compute alloc space references associated with this card.
@@ -248,7 +275,7 @@ void ModUnionTableReferenceCache::Update() {
auto* space = heap->FindContinuousSpaceFromObject(reinterpret_cast<Object*>(start), false);
DCHECK(space != nullptr);
SpaceBitmap* live_bitmap = space->GetLiveBitmap();
- live_bitmap->VisitMarkedRange(start, end, visitor);
+ live_bitmap->VisitMarkedRange(start, end, add_visitor);
// Update the corresponding references for the card.
auto found = references_.find(card);
@@ -263,46 +290,41 @@ void ModUnionTableReferenceCache::Update() {
}
}
cleared_cards_.clear();
-}
-
-void ModUnionTableReferenceCache::MarkReferences(collector::MarkSweep* mark_sweep) {
size_t count = 0;
-
for (const auto& ref : references_) {
- for (const auto& obj : ref.second) {
- mark_sweep->MarkRoot(obj);
- ++count;
+ for (const auto& obj_ptr : ref.second) {
+ Object* obj = *obj_ptr;
+ if (obj != nullptr) {
+ Object* new_obj = visitor(obj, arg);
+ // Avoid dirtying pages in the image unless necessary.
+ if (new_obj != obj) {
+ *obj_ptr = new_obj;
+ }
+ }
}
+ count += ref.second.size();
}
if (VLOG_IS_ON(heap)) {
VLOG(gc) << "Marked " << count << " references in mod union table";
}
}
-void ModUnionTableCardCache::ClearCards(space::ContinuousSpace* space) {
+void ModUnionTableCardCache::ClearCards() {
CardTable* card_table = GetHeap()->GetCardTable();
ModUnionClearCardSetVisitor visitor(&cleared_cards_);
// Clear dirty cards in the this space and update the corresponding mod-union bits.
- card_table->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), visitor);
+ card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
}
// Mark all references to the alloc space(s).
-void ModUnionTableCardCache::MarkReferences(collector::MarkSweep* mark_sweep) {
+void ModUnionTableCardCache::UpdateAndMarkReferences(RootVisitor visitor, void* arg) {
CardTable* card_table = heap_->GetCardTable();
- ModUnionScanImageRootVisitor visitor(mark_sweep);
- space::ContinuousSpace* space = nullptr;
- SpaceBitmap* bitmap = nullptr;
+ ModUnionScanImageRootVisitor scan_visitor(visitor, arg);
+ SpaceBitmap* bitmap = space_->GetLiveBitmap();
for (const byte* card_addr : cleared_cards_) {
- auto start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
- auto end = start + CardTable::kCardSize;
- auto obj_start = reinterpret_cast<Object*>(start);
- if (UNLIKELY(space == nullptr || !space->Contains(obj_start))) {
- space = heap_->FindContinuousSpaceFromObject(obj_start, false);
- DCHECK(space != nullptr);
- bitmap = space->GetLiveBitmap();
- DCHECK(bitmap != nullptr);
- }
- bitmap->VisitMarkedRange(start, end, visitor);
+ uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
+ DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
+ bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
}
}
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index eb7a754d24..5a99f1bb41 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -19,6 +19,7 @@
#include "gc_allocator.h"
#include "globals.h"
+#include "root_visitor.h"
#include "safe_map.h"
#include <set>
@@ -50,23 +51,25 @@ class HeapBitmap;
// cleared between GC phases, reducing the number of dirty cards that need to be scanned.
class ModUnionTable {
public:
- typedef std::set<byte*, std::less<byte*>, GCAllocator<byte*> > CardSet;
+ typedef std::set<byte*, std::less<byte*>, GcAllocator<byte*> > CardSet;
- explicit ModUnionTable(Heap* heap) : heap_(heap) {}
+ explicit ModUnionTable(const std::string& name, Heap* heap, space::ContinuousSpace* space)
+ : name_(name),
+ heap_(heap),
+ space_(space) {
+ }
virtual ~ModUnionTable() {}
// Clear cards which map to a memory range of a space. This doesn't immediately update the
// mod-union table, as updating the mod-union table may have an associated cost, such as
// determining references to track.
- virtual void ClearCards(space::ContinuousSpace* space) = 0;
+ virtual void ClearCards() = 0;
// Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards
- // before a call to update, for example, back-to-back sticky GCs.
- virtual void Update() = 0;
-
- // Mark the bitmaps for all references which are stored in the mod-union table.
- virtual void MarkReferences(collector::MarkSweep* mark_sweep) = 0;
+ // before a call to update, for example, back-to-back sticky GCs. Also mark references to other
+ // spaces which are stored in the mod-union table.
+ virtual void UpdateAndMarkReferences(RootVisitor visitor, void* arg) = 0;
// Verification, sanity checks that we don't have clean cards which conflict with out cached data
// for said cards. Exclusive lock is required since verify sometimes uses
@@ -75,31 +78,35 @@ class ModUnionTable {
virtual void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) = 0;
virtual void Dump(std::ostream& os) = 0;
-
+ space::ContinuousSpace* GetSpace() {
+ return space_;
+ }
Heap* GetHeap() const {
return heap_;
}
+ const std::string& GetName() const {
+ return name_;
+ }
protected:
+ const std::string name_;
Heap* const heap_;
+ space::ContinuousSpace* const space_;
};
// Reference caching implementation. Caches references pointing to alloc space(s) for each card.
class ModUnionTableReferenceCache : public ModUnionTable {
public:
- explicit ModUnionTableReferenceCache(Heap* heap) : ModUnionTable(heap) {}
+ explicit ModUnionTableReferenceCache(const std::string& name, Heap* heap,
+ space::ContinuousSpace* space)
+ : ModUnionTable(name, heap, space) {}
virtual ~ModUnionTableReferenceCache() {}
// Clear and store cards for a space.
- void ClearCards(space::ContinuousSpace* space);
+ void ClearCards();
- // Update table based on cleared cards.
- void Update()
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- // Mark all references to the alloc space(s).
- void MarkReferences(collector::MarkSweep* mark_sweep)
+ // Update table based on cleared cards and mark all references to the other spaces.
+ void UpdateAndMarkReferences(RootVisitor visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -117,24 +124,22 @@ class ModUnionTableReferenceCache : public ModUnionTable {
ModUnionTable::CardSet cleared_cards_;
// Maps from dirty cards to their corresponding alloc space references.
- SafeMap<const byte*, std::vector<const mirror::Object*>, std::less<const byte*>,
- GCAllocator<std::pair<const byte*, std::vector<const mirror::Object*> > > > references_;
+ SafeMap<const byte*, std::vector<mirror::Object**>, std::less<const byte*>,
+ GcAllocator<std::pair<const byte*, std::vector<mirror::Object**> > > > references_;
};
// Card caching implementation. Keeps track of which cards we cleared and only this information.
class ModUnionTableCardCache : public ModUnionTable {
public:
- explicit ModUnionTableCardCache(Heap* heap) : ModUnionTable(heap) {}
+ explicit ModUnionTableCardCache(const std::string& name, Heap* heap, space::ContinuousSpace* space)
+ : ModUnionTable(name, heap, space) {}
virtual ~ModUnionTableCardCache() {}
// Clear and store cards for a space.
- void ClearCards(space::ContinuousSpace* space);
-
- // Nothing to update as all dirty cards were placed into cleared cards during clearing.
- void Update() {}
+ void ClearCards();
// Mark all references to the alloc space(s).
- void MarkReferences(collector::MarkSweep* mark_sweep)
+ void UpdateAndMarkReferences(RootVisitor visitor, void* arg)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 63b24ffb4e..52c02f7728 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -62,9 +62,11 @@ SpaceBitmap* SpaceBitmap::Create(const std::string& name, byte* heap_begin, size
CHECK(heap_begin != NULL);
// Round up since heap_capacity is not necessarily a multiple of kAlignment * kBitsPerWord.
size_t bitmap_size = OffsetToIndex(RoundUp(heap_capacity, kAlignment * kBitsPerWord)) * kWordSize;
- UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), NULL, bitmap_size, PROT_READ | PROT_WRITE));
- if (mem_map.get() == NULL) {
- LOG(ERROR) << "Failed to allocate bitmap " << name;
+ std::string error_msg;
+ UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), NULL, bitmap_size,
+ PROT_READ | PROT_WRITE, &error_msg));
+ if (UNLIKELY(mem_map.get() == nullptr)) {
+ LOG(ERROR) << "Failed to allocate bitmap " << name << ": " << error_msg;
return NULL;
}
return CreateFromMemMap(name, mem_map.release(), heap_begin, heap_capacity);
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index f975692a3f..21709ad3b7 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -212,7 +212,7 @@ class SpaceSetMap {
public:
typedef std::set<
const mirror::Object*, std::less<const mirror::Object*>,
- GCAllocator<const mirror::Object*> > Objects;
+ GcAllocator<const mirror::Object*> > Objects;
bool IsEmpty() const {
return contained_.empty();
@@ -247,8 +247,8 @@ class SpaceSetMap {
template <typename Visitor>
void Visit(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS {
- for (Objects::iterator it = contained_.begin(); it != contained_.end(); ++it) {
- visitor(*it);
+ for (const mirror::Object* obj : contained_) {
+ visitor(const_cast<mirror::Object*>(obj));
}
}
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
index 3cc64e9da5..a6a3ee7a57 100644
--- a/runtime/gc/allocator/dlmalloc.cc
+++ b/runtime/gc/allocator/dlmalloc.cc
@@ -69,3 +69,19 @@ extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_byte
*reclaimed += length;
}
}
+
+extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* bytes_allocated = reinterpret_cast<size_t*>(arg);
+ *bytes_allocated += used_bytes + sizeof(size_t);
+}
+
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* objects_allocated = reinterpret_cast<size_t*>(arg);
+ ++(*objects_allocated);
+}
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
index 07ebd1c0e3..c820b192b9 100644
--- a/runtime/gc/allocator/dlmalloc.h
+++ b/runtime/gc/allocator/dlmalloc.h
@@ -18,6 +18,8 @@
#define ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
// Configure dlmalloc for mspaces.
+// Avoid a collision with one used in llvm.
+#undef HAVE_MMAP
#define HAVE_MMAP 0
#define HAVE_MREMAP 0
#define HAVE_MORECORE 1
@@ -37,4 +39,10 @@ extern "C" int dlmalloc_trim(size_t);
// pages back to the kernel.
extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
+// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will
+// count the number of bytes allocated and objects allocated,
+// respectively.
+extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+
#endif // ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h
index d0b0b5c930..270c9efde9 100644
--- a/runtime/gc/collector/mark_sweep-inl.h
+++ b/runtime/gc/collector/mark_sweep-inl.h
@@ -29,7 +29,7 @@ namespace gc {
namespace collector {
template <typename MarkVisitor>
-inline void MarkSweep::ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) {
+inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) {
DCHECK(obj != NULL);
if (kIsDebugBuild && !IsMarked(obj)) {
heap_->DumpSpaces();
@@ -62,7 +62,8 @@ inline void MarkSweep::ScanObjectVisit(const mirror::Object* obj, const MarkVisi
}
template <typename Visitor>
-inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Visitor& visitor)
+inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& visitor,
+ bool visit_class)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_) {
DCHECK(obj != NULL);
@@ -70,6 +71,9 @@ inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Vi
mirror::Class* klass = obj->GetClass();
DCHECK(klass != NULL);
+ if (visit_class) {
+ visitor(obj, klass, MemberOffset(0), false);
+ }
if (klass == mirror::Class::GetJavaLangClass()) {
DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass());
VisitClassReferences(klass, obj, visitor);
@@ -86,8 +90,8 @@ inline void MarkSweep::VisitObjectReferences(const mirror::Object* obj, const Vi
}
template <typename Visitor>
-inline void MarkSweep::VisitInstanceFieldsReferences(const mirror::Class* klass,
- const mirror::Object* obj,
+inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass,
+ mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
DCHECK(obj != NULL);
@@ -96,7 +100,7 @@ inline void MarkSweep::VisitInstanceFieldsReferences(const mirror::Class* klass,
}
template <typename Visitor>
-inline void MarkSweep::VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj,
+inline void MarkSweep::VisitClassReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
VisitInstanceFieldsReferences(klass, obj, visitor);
@@ -104,15 +108,14 @@ inline void MarkSweep::VisitClassReferences(const mirror::Class* klass, const mi
}
template <typename Visitor>
-inline void MarkSweep::VisitStaticFieldsReferences(const mirror::Class* klass,
- const Visitor& visitor)
+inline void MarkSweep::VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
DCHECK(klass != NULL);
VisitFieldsReferences(klass, klass->GetReferenceStaticOffsets(), true, visitor);
}
template <typename Visitor>
-inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets,
+inline void MarkSweep::VisitFieldsReferences(mirror::Object* obj, uint32_t ref_offsets,
bool is_static, const Visitor& visitor) {
if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) {
// Found a reference offset bitmap. Mark the specified offsets.
@@ -124,7 +127,7 @@ inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t
while (ref_offsets != 0) {
size_t right_shift = CLZ(ref_offsets);
MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift);
- const mirror::Object* ref = obj->GetFieldObject<const mirror::Object*>(field_offset, false);
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object*>(field_offset, false);
visitor(obj, ref, field_offset, is_static);
ref_offsets &= ~(CLASS_HIGH_BIT >> right_shift);
}
@@ -143,7 +146,7 @@ inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t
mirror::ArtField* field = (is_static ? klass->GetStaticField(i)
: klass->GetInstanceField(i));
MemberOffset field_offset = field->GetOffset();
- const mirror::Object* ref = obj->GetFieldObject<const mirror::Object*>(field_offset, false);
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object*>(field_offset, false);
visitor(obj, ref, field_offset, is_static);
}
}
@@ -151,11 +154,11 @@ inline void MarkSweep::VisitFieldsReferences(const mirror::Object* obj, uint32_t
}
template <typename Visitor>
-inline void MarkSweep::VisitObjectArrayReferences(const mirror::ObjectArray<mirror::Object>* array,
+inline void MarkSweep::VisitObjectArrayReferences(mirror::ObjectArray<mirror::Object>* array,
const Visitor& visitor) {
const size_t length = static_cast<size_t>(array->GetLength());
for (size_t i = 0; i < length; ++i) {
- const mirror::Object* element = array->GetWithoutChecks(static_cast<int32_t>(i));
+ mirror::Object* element = array->GetWithoutChecks(static_cast<int32_t>(i));
const size_t width = sizeof(mirror::Object*);
MemberOffset offset(i * width + mirror::Array::DataOffset(width).Int32Value());
visitor(array, element, offset, false);
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6790144603..2c69c77187 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -28,6 +28,7 @@
#include "base/timing_logger.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
+#include "gc/accounting/mod_union_table.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
@@ -99,7 +100,7 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) {
} else {
const space::ContinuousSpace* prev_space = nullptr;
// Find out if the previous space is immune.
- for (space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) {
+ for (const space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) {
if (cur_space == space) {
break;
}
@@ -107,15 +108,19 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) {
}
// If previous space was immune, then extend the immune region. Relies on continuous spaces
// being sorted by Heap::AddContinuousSpace.
- if (prev_space != NULL &&
- immune_begin_ <= reinterpret_cast<Object*>(prev_space->Begin()) &&
- immune_end_ >= reinterpret_cast<Object*>(prev_space->End())) {
+ if (prev_space != NULL && IsImmuneSpace(prev_space)) {
immune_begin_ = std::min(reinterpret_cast<Object*>(space->Begin()), immune_begin_);
immune_end_ = std::max(reinterpret_cast<Object*>(space->End()), immune_end_);
}
}
}
+bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) {
+ return
+ immune_begin_ <= reinterpret_cast<Object*>(space->Begin()) &&
+ immune_end_ >= reinterpret_cast<Object*>(space->End());
+}
+
void MarkSweep::BindBitmaps() {
timings_.StartSplit("BindBitmaps");
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
@@ -263,11 +268,23 @@ void MarkSweep::MarkingPhase() {
}
live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
MarkConcurrentRoots();
-
- heap_->UpdateAndMarkModUnion(this, timings_, GetGcType());
+ UpdateAndMarkModUnion();
MarkReachableObjects();
}
+void MarkSweep::UpdateAndMarkModUnion() {
+ for (const auto& space : heap_->GetContinuousSpaces()) {
+ if (IsImmuneSpace(space)) {
+ const char* name = space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" :
+ "UpdateAndMarkImageModUnionTable";
+ base::TimingLogger::ScopedSplit split(name, &timings_);
+ accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space);
+ CHECK(mod_union_table != nullptr);
+ mod_union_table->UpdateAndMarkReferences(MarkRootCallback, this);
+ }
+ }
+}
+
void MarkSweep::MarkThreadRoots(Thread* self) {
MarkRootsCheckpoint(self);
}
@@ -519,24 +536,18 @@ void MarkSweep::MarkRoot(const Object* obj) {
}
}
-void MarkSweep::MarkRootParallelCallback(const Object* root, void* arg) {
+Object* MarkSweep::MarkRootParallelCallback(Object* root, void* arg) {
DCHECK(root != NULL);
DCHECK(arg != NULL);
reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(root);
+ return root;
}
-void MarkSweep::MarkObjectCallback(const Object* root, void* arg) {
- DCHECK(root != NULL);
- DCHECK(arg != NULL);
- MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg);
- mark_sweep->MarkObjectNonNull(root);
-}
-
-void MarkSweep::ReMarkObjectVisitor(const Object* root, void* arg) {
- DCHECK(root != NULL);
- DCHECK(arg != NULL);
- MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg);
- mark_sweep->MarkObjectNonNull(root);
+Object* MarkSweep::MarkRootCallback(Object* root, void* arg) {
+ DCHECK(root != nullptr);
+ DCHECK(arg != nullptr);
+ reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(root);
+ return root;
}
void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
@@ -564,30 +575,30 @@ void MarkSweep::VerifyRoots() {
// Marks all objects in the root set.
void MarkSweep::MarkRoots() {
timings_.StartSplit("MarkRoots");
- Runtime::Current()->VisitNonConcurrentRoots(MarkObjectCallback, this);
+ Runtime::Current()->VisitNonConcurrentRoots(MarkRootCallback, this);
timings_.EndSplit();
}
void MarkSweep::MarkNonThreadRoots() {
timings_.StartSplit("MarkNonThreadRoots");
- Runtime::Current()->VisitNonThreadRoots(MarkObjectCallback, this);
+ Runtime::Current()->VisitNonThreadRoots(MarkRootCallback, this);
timings_.EndSplit();
}
void MarkSweep::MarkConcurrentRoots() {
timings_.StartSplit("MarkConcurrentRoots");
// Visit all runtime roots and clear dirty flags.
- Runtime::Current()->VisitConcurrentRoots(MarkObjectCallback, this, false, true);
+ Runtime::Current()->VisitConcurrentRoots(MarkRootCallback, this, false, true);
timings_.EndSplit();
}
void MarkSweep::CheckObject(const Object* obj) {
DCHECK(obj != NULL);
- VisitObjectReferences(obj, [this](const Object* obj, const Object* ref, MemberOffset offset,
- bool is_static) NO_THREAD_SAFETY_ANALYSIS {
+ VisitObjectReferences(const_cast<Object*>(obj), [this](const Object* obj, const Object* ref,
+ MemberOffset offset, bool is_static) NO_THREAD_SAFETY_ANALYSIS {
Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current());
CheckReference(obj, ref, offset, is_static);
- });
+ }, true);
}
void MarkSweep::VerifyImageRootVisitor(Object* root, void* arg) {
@@ -653,11 +664,11 @@ class MarkStackTask : public Task {
explicit ScanObjectParallelVisitor(MarkStackTask<kUseFinger>* chunk_task) ALWAYS_INLINE
: chunk_task_(chunk_task) {}
- void operator()(const Object* obj) const {
+ void operator()(Object* obj) const {
MarkSweep* mark_sweep = chunk_task_->mark_sweep_;
mark_sweep->ScanObjectVisit(obj,
- [mark_sweep, this](const Object* /* obj */, const Object* ref,
- const MemberOffset& /* offset */, bool /* is_static */) ALWAYS_INLINE {
+ [mark_sweep, this](Object* /* obj */, Object* ref, const MemberOffset& /* offset */,
+ bool /* is_static */) ALWAYS_INLINE {
if (ref != nullptr && mark_sweep->MarkObjectParallel(ref)) {
if (kUseFinger) {
android_memory_barrier();
@@ -714,11 +725,11 @@ class MarkStackTask : public Task {
static const size_t kFifoSize = 4;
BoundedFifoPowerOfTwo<const Object*, kFifoSize> prefetch_fifo;
for (;;) {
- const Object* obj = NULL;
+ const Object* obj = nullptr;
if (kUseMarkStackPrefetch) {
while (mark_stack_pos_ != 0 && prefetch_fifo.size() < kFifoSize) {
const Object* obj = mark_stack_[--mark_stack_pos_];
- DCHECK(obj != NULL);
+ DCHECK(obj != nullptr);
__builtin_prefetch(obj);
prefetch_fifo.push_back(obj);
}
@@ -733,8 +744,8 @@ class MarkStackTask : public Task {
}
obj = mark_stack_[--mark_stack_pos_];
}
- DCHECK(obj != NULL);
- visitor(obj);
+ DCHECK(obj != nullptr);
+ visitor(const_cast<mirror::Object*>(obj));
}
}
};
@@ -765,7 +776,6 @@ class CardScanTask : public MarkStackTask<false> {
ScanObjectParallelVisitor visitor(this);
accounting::CardTable* card_table = mark_sweep_->GetHeap()->GetCardTable();
size_t cards_scanned = card_table->Scan(bitmap_, begin_, end_, visitor, minimum_age_);
- mark_sweep_->cards_scanned_.fetch_add(cards_scanned);
VLOG(heap) << "Parallel scanning cards " << reinterpret_cast<void*>(begin_) << " - "
<< reinterpret_cast<void*>(end_) << " = " << cards_scanned;
// Finish by emptying our local mark stack.
@@ -803,11 +813,14 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) {
DCHECK_NE(mark_stack_tasks, 0U);
const size_t mark_stack_delta = std::min(CardScanTask::kMaxSize / 2,
mark_stack_size / mark_stack_tasks + 1);
- size_t ref_card_count = 0;
- cards_scanned_ = 0;
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
byte* card_begin = space->Begin();
byte* card_end = space->End();
+ // Align up the end address. For example, the image space's end
+ // may not be card-size-aligned.
+ card_end = AlignUp(card_end, accounting::CardTable::kCardSize);
+ DCHECK(IsAligned<accounting::CardTable::kCardSize>(card_begin));
+ DCHECK(IsAligned<accounting::CardTable::kCardSize>(card_end));
// Calculate how many bytes of heap we will scan,
const size_t address_range = card_end - card_begin;
// Calculate how much address range each task gets.
@@ -831,24 +844,15 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) {
thread_pool->AddTask(self, task);
card_begin += card_increment;
}
-
- if (paused && kIsDebugBuild) {
- // Make sure we don't miss scanning any cards.
- size_t scanned_cards = card_table->Scan(space->GetMarkBitmap(), space->Begin(),
- space->End(), VoidFunctor(), minimum_age);
- VLOG(heap) << "Scanning space cards " << reinterpret_cast<void*>(space->Begin()) << " - "
- << reinterpret_cast<void*>(space->End()) << " = " << scanned_cards;
- ref_card_count += scanned_cards;
- }
}
+ // Note: the card scan below may dirty new cards (and scan them)
+ // as a side effect when a Reference object is encountered and
+ // queued during the marking. See b/11465268.
thread_pool->SetMaxActiveWorkers(thread_count - 1);
thread_pool->StartWorkers(self);
thread_pool->Wait(self, true, true);
thread_pool->StopWorkers(self);
- if (paused) {
- DCHECK_EQ(ref_card_count, static_cast<size_t>(cards_scanned_.load()));
- }
timings_.EndSplit();
} else {
for (const auto& space : GetHeap()->GetContinuousSpaces()) {
@@ -990,8 +994,11 @@ void MarkSweep::RecursiveMark() {
ProcessMarkStack(false);
}
-bool MarkSweep::IsMarkedCallback(const Object* object, void* arg) {
- return reinterpret_cast<MarkSweep*>(arg)->IsMarked(object);
+mirror::Object* MarkSweep::SystemWeakIsMarkedCallback(Object* object, void* arg) {
+ if (reinterpret_cast<MarkSweep*>(arg)->IsMarked(object)) {
+ return object;
+ }
+ return nullptr;
}
void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) {
@@ -1001,45 +1008,21 @@ void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) {
void MarkSweep::ReMarkRoots() {
timings_.StartSplit("ReMarkRoots");
- Runtime::Current()->VisitRoots(ReMarkObjectVisitor, this, true, true);
+ Runtime::Current()->VisitRoots(MarkRootCallback, this, true, true);
timings_.EndSplit();
}
-void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) {
- Runtime::Current()->GetJavaVM()->SweepWeakGlobals(is_marked, arg);
-}
-
-struct ArrayMarkedCheck {
- accounting::ObjectStack* live_stack;
- MarkSweep* mark_sweep;
-};
-
-// Either marked or not live.
-bool MarkSweep::IsMarkedArrayCallback(const Object* object, void* arg) {
- ArrayMarkedCheck* array_check = reinterpret_cast<ArrayMarkedCheck*>(arg);
- if (array_check->mark_sweep->IsMarked(object)) {
- return true;
- }
- accounting::ObjectStack* live_stack = array_check->live_stack;
- if (std::find(live_stack->Begin(), live_stack->End(), object) == live_stack->End()) {
- return true;
- }
- return false;
-}
-
void MarkSweep::SweepSystemWeaks() {
Runtime* runtime = Runtime::Current();
timings_.StartSplit("SweepSystemWeaks");
- runtime->GetInternTable()->SweepInternTableWeaks(IsMarkedCallback, this);
- runtime->GetMonitorList()->SweepMonitorList(IsMarkedCallback, this);
- SweepJniWeakGlobals(IsMarkedCallback, this);
+ runtime->SweepSystemWeaks(SystemWeakIsMarkedCallback, this);
timings_.EndSplit();
}
-bool MarkSweep::VerifyIsLiveCallback(const Object* obj, void* arg) {
+mirror::Object* MarkSweep::VerifySystemWeakIsLiveCallback(Object* obj, void* arg) {
reinterpret_cast<MarkSweep*>(arg)->VerifyIsLive(obj);
// We don't actually want to sweep the object, so lets return "marked"
- return true;
+ return obj;
}
void MarkSweep::VerifyIsLive(const Object* obj) {
@@ -1058,11 +1041,8 @@ void MarkSweep::VerifyIsLive(const Object* obj) {
}
void MarkSweep::VerifySystemWeaks() {
- Runtime* runtime = Runtime::Current();
- // Verify system weaks, uses a special IsMarked callback which always returns true.
- runtime->GetInternTable()->SweepInternTableWeaks(VerifyIsLiveCallback, this);
- runtime->GetMonitorList()->SweepMonitorList(VerifyIsLiveCallback, this);
- runtime->GetJavaVM()->SweepWeakGlobals(VerifyIsLiveCallback, this);
+ // Verify system weaks, uses a special object visitor which returns the input object.
+ Runtime::Current()->SweepSystemWeaks(VerifySystemWeakIsLiveCallback, this);
}
struct SweepCallbackContext {
@@ -1369,10 +1349,6 @@ void MarkSweep::DelayReferenceReferent(mirror::Class* klass, Object* obj) {
}
}
-void MarkSweep::ScanRoot(const Object* obj) {
- ScanObject(obj);
-}
-
class MarkObjectVisitor {
public:
explicit MarkObjectVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) {}
@@ -1396,7 +1372,7 @@ class MarkObjectVisitor {
// and dispatches to a specialized scanning routine.
void MarkSweep::ScanObject(const Object* obj) {
MarkObjectVisitor visitor(this);
- ScanObjectVisit(obj, visitor);
+ ScanObjectVisit(const_cast<Object*>(obj), visitor);
}
void MarkSweep::ProcessMarkStackParallel(size_t thread_count) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index feef992c83..3bc014aa1e 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -69,7 +69,7 @@ class MarkSweep : public GarbageCollector {
virtual bool HandleDirtyObjectsPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
virtual void MarkingPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
virtual void ReclaimPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- virtual void FinishPhase();
+ virtual void FinishPhase() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
virtual void MarkReachableObjects()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -114,6 +114,9 @@ class MarkSweep : public GarbageCollector {
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool IsImmuneSpace(const space::ContinuousSpace* space)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie
// the image. Mark that portion of the heap as immune.
virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -137,6 +140,9 @@ class MarkSweep : public GarbageCollector {
void ProcessReferences(Thread* self)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ virtual void UpdateAndMarkModUnion()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Sweeps unmarked objects to complete the garbage collection.
virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
@@ -151,11 +157,6 @@ class MarkSweep : public GarbageCollector {
return cleared_reference_list_;
}
- // Proxy for external access to ScanObject.
- void ScanRoot(const mirror::Object* obj)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
// Blackens an object.
void ScanObject(const mirror::Object* obj)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
@@ -163,7 +164,7 @@ class MarkSweep : public GarbageCollector {
// TODO: enable thread safety analysis when in use by multiple worker threads.
template <typename MarkVisitor>
- void ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor)
+ void ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor)
NO_THREAD_SAFETY_ANALYSIS;
size_t GetFreedBytes() const {
@@ -202,28 +203,29 @@ class MarkSweep : public GarbageCollector {
void SetImmuneRange(mirror::Object* begin, mirror::Object* end);
void SweepSystemWeaks()
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
- static bool VerifyIsLiveCallback(const mirror::Object* obj, void* arg)
+ static mirror::Object* VerifySystemWeakIsLiveCallback(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
void VerifySystemWeaks()
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
// Verify that an object is live, either in a live bitmap or in the allocation stack.
void VerifyIsLive(const mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
template <typename Visitor>
- static void VisitObjectReferences(const mirror::Object* obj, const Visitor& visitor)
+ static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor,
+ bool visit_class = false)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_);
- static void MarkObjectCallback(const mirror::Object* root, void* arg)
+ static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void MarkRootParallelCallback(const mirror::Object* root, void* arg);
+ static mirror::Object* MarkRootParallelCallback(mirror::Object* root, void* arg);
// Marks an object.
void MarkObject(const mirror::Object* obj)
@@ -242,16 +244,12 @@ class MarkSweep : public GarbageCollector {
// Returns true if the object has its bit set in the mark bitmap.
bool IsMarked(const mirror::Object* object) const;
- static bool IsMarkedCallback(const mirror::Object* object, void* arg)
+ static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static bool IsMarkedArrayCallback(const mirror::Object* object, void* arg)
+ static mirror::Object* SystemWeakIsMarkedArrayCallback(mirror::Object* object, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- static void ReMarkObjectVisitor(const mirror::Object* root, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-
static void VerifyImageRootVisitor(mirror::Object* root, void* arg)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_,
Locks::mutator_lock_);
@@ -310,7 +308,7 @@ class MarkSweep : public GarbageCollector {
size_t GetThreadCount(bool paused) const;
// Returns true if an object is inside of the immune region (assumed to be marked).
- bool IsImmune(const mirror::Object* obj) const {
+ bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE {
return obj >= immune_begin_ && obj < immune_end_;
}
@@ -321,34 +319,34 @@ class MarkSweep : public GarbageCollector {
NO_THREAD_SAFETY_ANALYSIS;
template <typename Visitor>
- static void VisitInstanceFieldsReferences(const mirror::Class* klass, const mirror::Object* obj,
+ static void VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Visit the header, static field references, and interface pointers of a class object.
template <typename Visitor>
- static void VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj,
+ static void VisitClassReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
template <typename Visitor>
- static void VisitStaticFieldsReferences(const mirror::Class* klass, const Visitor& visitor)
+ static void VisitStaticFieldsReferences(mirror::Class* klass, const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
template <typename Visitor>
- static void VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets, bool is_static,
+ static void VisitFieldsReferences(mirror::Object* obj, uint32_t ref_offsets, bool is_static,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Visit all of the references in an object array.
template <typename Visitor>
- static void VisitObjectArrayReferences(const mirror::ObjectArray<mirror::Object>* array,
+ static void VisitObjectArrayReferences(mirror::ObjectArray<mirror::Object>* array,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
// Visits the header and field references of a data object.
template <typename Visitor>
- static void VisitOtherReferences(const mirror::Class* klass, const mirror::Object* obj,
+ static void VisitOtherReferences(mirror::Class* klass, mirror::Object* obj,
const Visitor& visitor)
SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
return VisitInstanceFieldsReferences(klass, obj, visitor);
@@ -390,9 +388,6 @@ class MarkSweep : public GarbageCollector {
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-
// Whether or not we count how many of each type of object were scanned.
static const bool kCountScannedTypes = false;
@@ -438,7 +433,6 @@ class MarkSweep : public GarbageCollector {
AtomicInteger work_chunks_created_;
AtomicInteger work_chunks_deleted_;
AtomicInteger reference_count_;
- AtomicInteger cards_scanned_;
// Verification.
size_t live_stack_freeze_size_;
diff --git a/runtime/gc/collector/sticky_mark_sweep.h b/runtime/gc/collector/sticky_mark_sweep.h
index 79c4359901..8bee00f0b8 100644
--- a/runtime/gc/collector/sticky_mark_sweep.h
+++ b/runtime/gc/collector/sticky_mark_sweep.h
@@ -31,6 +31,10 @@ class StickyMarkSweep : public PartialMarkSweep {
return kGcTypeSticky;
}
+ // Don't need to do anything special here since we scan all the cards which may have references
+ // to the newly allocated objects.
+ virtual void UpdateAndMarkModUnion() { }
+
explicit StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = "");
~StickyMarkSweep() {}
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
new file mode 100644
index 0000000000..873eadc46a
--- /dev/null
+++ b/runtime/gc/heap-inl.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2013 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_HEAP_INL_H_
+#define ART_RUNTIME_GC_HEAP_INL_H_
+
+#include "heap.h"
+
+#include "debugger.h"
+#include "gc/space/dlmalloc_space-inl.h"
+#include "gc/space/large_object_space.h"
+#include "object_utils.h"
+#include "runtime.h"
+#include "thread.h"
+#include "thread-inl.h"
+
+namespace art {
+namespace gc {
+
+inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count) {
+ DebugCheckPreconditionsForAllobObject(c, byte_count);
+ mirror::Object* obj;
+ size_t bytes_allocated;
+ AllocationTimer alloc_timer(this, &obj);
+ bool large_object_allocation = TryAllocLargeObjectUninstrumented(self, c, byte_count,
+ &obj, &bytes_allocated);
+ if (LIKELY(!large_object_allocation)) {
+ // Non-large object allocation.
+ obj = AllocateUninstrumented(self, alloc_space_, byte_count, &bytes_allocated);
+ // Ensure that we did not allocate into a zygote space.
+ DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace());
+ }
+ if (LIKELY(obj != NULL)) {
+ obj->SetClass(c);
+ // Record allocation after since we want to use the atomic add for the atomic fence to guard
+ // the SetClass since we do not want the class to appear NULL in another thread.
+ size_t new_num_bytes_allocated = RecordAllocationUninstrumented(bytes_allocated, obj);
+ DCHECK(!Dbg::IsAllocTrackingEnabled());
+ CheckConcurrentGC(self, new_num_bytes_allocated, obj);
+ if (kDesiredHeapVerification > kNoHeapVerification) {
+ VerifyObject(obj);
+ }
+ return obj;
+ }
+ ThrowOutOfMemoryError(self, byte_count, large_object_allocation);
+ return NULL;
+}
+
+inline size_t Heap::RecordAllocationUninstrumented(size_t size, mirror::Object* obj) {
+ DCHECK(obj != NULL);
+ DCHECK_GT(size, 0u);
+ size_t old_num_bytes_allocated = static_cast<size_t>(num_bytes_allocated_.fetch_add(size));
+
+ DCHECK(!Runtime::Current()->HasStatsEnabled());
+
+ // This is safe to do since the GC will never free objects which are neither in the allocation
+ // stack or the live bitmap.
+ while (!allocation_stack_->AtomicPushBack(obj)) {
+ CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
+ }
+
+ return old_num_bytes_allocated + size;
+}
+
+inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
+ if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
+ return NULL;
+ }
+ DCHECK(!running_on_valgrind_);
+ return space->Alloc(self, alloc_size, bytes_allocated);
+}
+
+// DlMallocSpace-specific version.
+inline mirror::Object* Heap::TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
+ if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
+ return NULL;
+ }
+ DCHECK(!running_on_valgrind_);
+ return space->AllocNonvirtual(self, alloc_size, bytes_allocated);
+}
+
+template <class T>
+inline mirror::Object* Heap::AllocateUninstrumented(Thread* self, T* space, size_t alloc_size,
+ size_t* bytes_allocated) {
+ // Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
+ // done in the runnable state where suspension is expected.
+ DCHECK_EQ(self->GetState(), kRunnable);
+ self->AssertThreadSuspensionIsAllowable();
+
+ mirror::Object* ptr = TryToAllocateUninstrumented(self, space, alloc_size, false, bytes_allocated);
+ if (LIKELY(ptr != NULL)) {
+ return ptr;
+ }
+ return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated);
+}
+
+inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated) {
+ bool large_object_allocation = ShouldAllocLargeObject(c, byte_count);
+ if (UNLIKELY(large_object_allocation)) {
+ mirror::Object* obj = AllocateUninstrumented(self, large_object_space_, byte_count, bytes_allocated);
+ // Make sure that our large object didn't get placed anywhere within the space interval or else
+ // it breaks the immune range.
+ DCHECK(obj == NULL ||
+ reinterpret_cast<byte*>(obj) < continuous_spaces_.front()->Begin() ||
+ reinterpret_cast<byte*>(obj) >= continuous_spaces_.back()->End());
+ *obj_ptr = obj;
+ }
+ return large_object_allocation;
+}
+
+inline void Heap::DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) {
+ DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
+ (c->IsVariableSize() || c->GetObjectSize() == byte_count) ||
+ strlen(ClassHelper(c).GetDescriptor()) == 0);
+ DCHECK_GE(byte_count, sizeof(mirror::Object));
+}
+
+inline Heap::AllocationTimer::AllocationTimer(Heap* heap, mirror::Object** allocated_obj_ptr)
+ : heap_(heap), allocated_obj_ptr_(allocated_obj_ptr) {
+ if (kMeasureAllocationTime) {
+ allocation_start_time_ = NanoTime() / kTimeAdjust;
+ }
+}
+
+inline Heap::AllocationTimer::~AllocationTimer() {
+ if (kMeasureAllocationTime) {
+ mirror::Object* allocated_obj = *allocated_obj_ptr_;
+ // Only if the allocation succeeded, record the time.
+ if (allocated_obj != NULL) {
+ uint64_t allocation_end_time = NanoTime() / kTimeAdjust;
+ heap_->total_allocation_time_.fetch_add(allocation_end_time - allocation_start_time_);
+ }
+ }
+};
+
+inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) {
+ // We need to have a zygote space or else our newly allocated large object can end up in the
+ // Zygote resulting in it being prematurely freed.
+ // We can only do this for primitive objects since large objects will not be within the card table
+ // range. This also means that we rely on SetClass not dirtying the object's card.
+ return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray();
+}
+
+inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) {
+ size_t new_footprint = num_bytes_allocated_ + alloc_size;
+ if (UNLIKELY(new_footprint > max_allowed_footprint_)) {
+ if (UNLIKELY(new_footprint > growth_limit_)) {
+ return true;
+ }
+ if (!concurrent_gc_) {
+ if (!grow) {
+ return true;
+ } else {
+ max_allowed_footprint_ = new_footprint;
+ }
+ }
+ }
+ return false;
+}
+
+inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj) {
+ if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) {
+ // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint.
+ SirtRef<mirror::Object> ref(self, obj);
+ RequestConcurrentGC(self);
+ }
+}
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_HEAP_INL_H_
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 1b46257c08..804c66932e 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -39,6 +39,7 @@
#include "gc/space/image_space.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
+#include "heap-inl.h"
#include "image.h"
#include "invoke_arg_array_builder.h"
#include "mirror/art_field-inl.h"
@@ -63,11 +64,9 @@ static constexpr size_t kGcAlotInterval = KB;
static constexpr bool kDumpGcPerformanceOnShutdown = false;
// Minimum amount of remaining bytes before a concurrent GC is triggered.
static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB;
-// If true, measure the total allocation time.
-static constexpr bool kMeasureAllocationTime = false;
Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
- double target_utilization, size_t capacity, const std::string& original_image_file_name,
+ double target_utilization, size_t capacity, const std::string& image_file_name,
bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads,
bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold,
bool ignore_max_footprint)
@@ -105,7 +104,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
: std::numeric_limits<size_t>::max()),
total_bytes_freed_ever_(0),
total_objects_freed_ever_(0),
- large_object_threshold_(3 * kPageSize),
num_bytes_allocated_(0),
native_bytes_allocated_(0),
gc_memory_overhead_(0),
@@ -146,9 +144,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
// Requested begin for the alloc space, to follow the mapped image and oat files
byte* requested_alloc_space_begin = NULL;
- std::string image_file_name(original_image_file_name);
if (!image_file_name.empty()) {
- space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name);
+ space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str());
CHECK(image_space != NULL) << "Failed to create space for " << image_file_name;
AddContinuousSpace(image_space);
// Oat files referenced by image files immediately follow them in memory, ensure alloc space
@@ -191,11 +188,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
CHECK(card_table_.get() != NULL) << "Failed to create card table";
- image_mod_union_table_.reset(new accounting::ModUnionTableToZygoteAllocspace(this));
- CHECK(image_mod_union_table_.get() != NULL) << "Failed to create image mod-union table";
-
- zygote_mod_union_table_.reset(new accounting::ModUnionTableCardCache(this));
- CHECK(zygote_mod_union_table_.get() != NULL) << "Failed to create Zygote mod-union table";
+ accounting::ModUnionTable* mod_union_table =
+ new accounting::ModUnionTableToZygoteAllocspace("Image mod-union table", this,
+ GetImageSpace());
+ CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
+ AddModUnionTable(mod_union_table);
// TODO: Count objects in the image space here.
num_bytes_allocated_ = 0;
@@ -238,6 +235,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
}
CHECK_NE(max_allowed_footprint_, 0U);
+
+ if (running_on_valgrind_) {
+ Runtime::Current()->InstrumentQuickAllocEntryPoints();
+ }
+
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() exiting";
}
@@ -489,10 +491,7 @@ Heap::~Heap() {
live_stack_->Reset();
VLOG(heap) << "~Heap()";
- // We can't take the heap lock here because there might be a daemon thread suspended with the
- // heap lock held. We know though that no non-daemon threads are executing, and we know that
- // all daemon threads are suspended, and we also know that the threads list have been deleted, so
- // those threads can't resume. We're the only running thread, and we can do whatever we like...
+ STLDeleteValues(&mod_union_tables_);
STLDeleteElements(&continuous_spaces_);
STLDeleteElements(&discontinuous_spaces_);
delete gc_complete_lock_;
@@ -554,81 +553,69 @@ static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void*
}
}
-mirror::Object* Heap::AllocObject(Thread* self, mirror::Class* c, size_t byte_count) {
- DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
- (c->IsVariableSize() || c->GetObjectSize() == byte_count) ||
- strlen(ClassHelper(c).GetDescriptor()) == 0);
- DCHECK_GE(byte_count, sizeof(mirror::Object));
-
- mirror::Object* obj = NULL;
- size_t bytes_allocated = 0;
- uint64_t allocation_start = 0;
- if (UNLIKELY(kMeasureAllocationTime)) {
- allocation_start = NanoTime() / kTimeAdjust;
+void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) {
+ std::ostringstream oss;
+ int64_t total_bytes_free = GetFreeMemory();
+ oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
+ << " free bytes";
+ // If the allocation failed due to fragmentation, print out the largest continuous allocation.
+ if (!large_object_allocation && total_bytes_free >= byte_count) {
+ size_t max_contiguous_allocation = 0;
+ for (const auto& space : continuous_spaces_) {
+ if (space->IsDlMallocSpace()) {
+ space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation);
+ }
+ }
+ oss << "; failed due to fragmentation (largest possible contiguous allocation "
+ << max_contiguous_allocation << " bytes)";
}
+ self->ThrowOutOfMemoryError(oss.str().c_str());
+}
- // We need to have a zygote space or else our newly allocated large object can end up in the
- // Zygote resulting in it being prematurely freed.
- // We can only do this for primitive objects since large objects will not be within the card table
- // range. This also means that we rely on SetClass not dirtying the object's card.
- bool large_object_allocation =
- byte_count >= large_object_threshold_ && have_zygote_space_ && c->IsPrimitiveArray();
+inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated) {
+ bool large_object_allocation = ShouldAllocLargeObject(c, byte_count);
if (UNLIKELY(large_object_allocation)) {
- obj = Allocate(self, large_object_space_, byte_count, &bytes_allocated);
+ mirror::Object* obj = AllocateInstrumented(self, large_object_space_, byte_count, bytes_allocated);
// Make sure that our large object didn't get placed anywhere within the space interval or else
// it breaks the immune range.
DCHECK(obj == NULL ||
reinterpret_cast<byte*>(obj) < continuous_spaces_.front()->Begin() ||
reinterpret_cast<byte*>(obj) >= continuous_spaces_.back()->End());
- } else {
- obj = Allocate(self, alloc_space_, byte_count, &bytes_allocated);
+ *obj_ptr = obj;
+ }
+ return large_object_allocation;
+}
+
+mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count) {
+ DebugCheckPreconditionsForAllobObject(c, byte_count);
+ mirror::Object* obj;
+ size_t bytes_allocated;
+ AllocationTimer alloc_timer(this, &obj);
+ bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count,
+ &obj, &bytes_allocated);
+ if (LIKELY(!large_object_allocation)) {
+ // Non-large object allocation.
+ obj = AllocateInstrumented(self, alloc_space_, byte_count, &bytes_allocated);
// Ensure that we did not allocate into a zygote space.
DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace());
}
-
if (LIKELY(obj != NULL)) {
obj->SetClass(c);
-
// Record allocation after since we want to use the atomic add for the atomic fence to guard
// the SetClass since we do not want the class to appear NULL in another thread.
- RecordAllocation(bytes_allocated, obj);
-
+ size_t new_num_bytes_allocated = RecordAllocationInstrumented(bytes_allocated, obj);
if (Dbg::IsAllocTrackingEnabled()) {
Dbg::RecordAllocation(c, byte_count);
}
- if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_) >= concurrent_start_bytes_)) {
- // The SirtRef is necessary since the calls in RequestConcurrentGC are a safepoint.
- SirtRef<mirror::Object> ref(self, obj);
- RequestConcurrentGC(self);
- }
+ CheckConcurrentGC(self, new_num_bytes_allocated, obj);
if (kDesiredHeapVerification > kNoHeapVerification) {
VerifyObject(obj);
}
-
- if (UNLIKELY(kMeasureAllocationTime)) {
- total_allocation_time_.fetch_add(NanoTime() / kTimeAdjust - allocation_start);
- }
-
return obj;
- } else {
- std::ostringstream oss;
- int64_t total_bytes_free = GetFreeMemory();
- oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free
- << " free bytes";
- // If the allocation failed due to fragmentation, print out the largest continuous allocation.
- if (!large_object_allocation && total_bytes_free >= byte_count) {
- size_t max_contiguous_allocation = 0;
- for (const auto& space : continuous_spaces_) {
- if (space->IsDlMallocSpace()) {
- space->AsDlMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation);
- }
- }
- oss << "; failed due to fragmentation (largest possible contiguous allocation "
- << max_contiguous_allocation << " bytes)";
- }
- self->ThrowOutOfMemoryError(oss.str().c_str());
- return NULL;
}
+ ThrowOutOfMemoryError(self, byte_count, large_object_allocation);
+ return NULL;
}
bool Heap::IsHeapAddress(const mirror::Object* obj) {
@@ -771,10 +758,10 @@ void Heap::VerifyHeap() {
GetLiveBitmap()->Walk(Heap::VerificationCallback, this);
}
-inline void Heap::RecordAllocation(size_t size, mirror::Object* obj) {
+inline size_t Heap::RecordAllocationInstrumented(size_t size, mirror::Object* obj) {
DCHECK(obj != NULL);
DCHECK_GT(size, 0u);
- num_bytes_allocated_.fetch_add(size);
+ size_t old_num_bytes_allocated = static_cast<size_t>(num_bytes_allocated_.fetch_add(size));
if (Runtime::Current()->HasStatsEnabled()) {
RuntimeStats* thread_stats = Thread::Current()->GetStats();
@@ -792,6 +779,8 @@ inline void Heap::RecordAllocation(size_t size, mirror::Object* obj) {
while (!allocation_stack_->AtomicPushBack(obj)) {
CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
}
+
+ return old_num_bytes_allocated + size;
}
void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) {
@@ -810,25 +799,8 @@ void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) {
}
}
-inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow) {
- size_t new_footprint = num_bytes_allocated_ + alloc_size;
- if (UNLIKELY(new_footprint > max_allowed_footprint_)) {
- if (UNLIKELY(new_footprint > growth_limit_)) {
- return true;
- }
- if (!concurrent_gc_) {
- if (!grow) {
- return true;
- } else {
- max_allowed_footprint_ = new_footprint;
- }
- }
- }
- return false;
-}
-
-inline mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size,
- bool grow, size_t* bytes_allocated) {
+inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
return NULL;
}
@@ -836,8 +808,8 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, space::AllocSpace* spac
}
// DlMallocSpace-specific version.
-inline mirror::Object* Heap::TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
- bool grow, size_t* bytes_allocated) {
+inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated) {
if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) {
return NULL;
}
@@ -849,15 +821,15 @@ inline mirror::Object* Heap::TryToAllocate(Thread* self, space::DlMallocSpace* s
}
template <class T>
-inline mirror::Object* Heap::Allocate(Thread* self, T* space, size_t alloc_size,
- size_t* bytes_allocated) {
+inline mirror::Object* Heap::AllocateInstrumented(Thread* self, T* space, size_t alloc_size,
+ size_t* bytes_allocated) {
// Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
// done in the runnable state where suspension is expected.
DCHECK_EQ(self->GetState(), kRunnable);
self->AssertThreadSuspensionIsAllowable();
- mirror::Object* ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
- if (ptr != NULL) {
+ mirror::Object* ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated);
+ if (LIKELY(ptr != NULL)) {
return ptr;
}
return AllocateInternalWithGc(self, space, alloc_size, bytes_allocated);
@@ -872,7 +844,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp
collector::GcType last_gc = WaitForConcurrentGcToComplete(self);
if (last_gc != collector::kGcTypeNone) {
// A GC was in progress and we blocked, retry allocation now that memory has been freed.
- ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
+ ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
@@ -907,7 +879,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp
i = static_cast<size_t>(gc_type_ran);
// Did we free sufficient memory for the allocation to succeed?
- ptr = TryToAllocate(self, space, alloc_size, false, bytes_allocated);
+ ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
@@ -916,7 +888,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp
// Allocations have failed after GCs; this is an exceptional state.
// Try harder, growing the heap if necessary.
- ptr = TryToAllocate(self, space, alloc_size, true, bytes_allocated);
+ ptr = TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated);
if (ptr != NULL) {
return ptr;
}
@@ -931,7 +903,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp
// We don't need a WaitForConcurrentGcToComplete here either.
CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true);
- return TryToAllocate(self, space, alloc_size, true, bytes_allocated);
+ return TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated);
}
void Heap::SetTargetHeapUtilization(float target) {
@@ -1084,15 +1056,15 @@ class ReferringObjectsFinder {
// For bitmap Visit.
// TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
// annotalysis on visitors.
- void operator()(const mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS {
- collector::MarkSweep::VisitObjectReferences(o, *this);
+ void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
+ collector::MarkSweep::VisitObjectReferences(obj, *this, true);
}
// For MarkSweep::VisitObjectReferences.
- void operator()(const mirror::Object* referrer, const mirror::Object* object,
+ void operator()(mirror::Object* referrer, mirror::Object* object,
const MemberOffset&, bool) const {
if (object == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
- referring_objects_.push_back(const_cast<mirror::Object*>(referrer));
+ referring_objects_.push_back(referrer);
}
}
@@ -1157,6 +1129,12 @@ void Heap::PreZygoteFork() {
AddContinuousSpace(alloc_space_);
have_zygote_space_ = true;
+ // Create the zygote space mod union table.
+ accounting::ModUnionTable* mod_union_table =
+ new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space);
+ CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
+ AddModUnionTable(mod_union_table);
+
// Reset the cumulative loggers since we now have a few additional timing phases.
for (const auto& collector : mark_sweep_collectors_) {
collector->ResetCumulativeStatistics();
@@ -1313,38 +1291,12 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus
return gc_type;
}
-void Heap::UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings,
- collector::GcType gc_type) {
- if (gc_type == collector::kGcTypeSticky) {
- // Don't need to do anything for mod union table in this case since we are only scanning dirty
- // cards.
- return;
- }
-
- base::TimingLogger::ScopedSplit split("UpdateModUnionTable", &timings);
- // Update zygote mod union table.
- if (gc_type == collector::kGcTypePartial) {
- base::TimingLogger::ScopedSplit split("UpdateZygoteModUnionTable", &timings);
- zygote_mod_union_table_->Update();
-
- timings.NewSplit("ZygoteMarkReferences");
- zygote_mod_union_table_->MarkReferences(mark_sweep);
- }
-
- // Processes the cards we cleared earlier and adds their objects into the mod-union table.
- timings.NewSplit("UpdateModUnionTable");
- image_mod_union_table_->Update();
-
- // Scans all objects in the mod-union table.
- timings.NewSplit("MarkImageToAllocSpaceReferences");
- image_mod_union_table_->MarkReferences(mark_sweep);
-}
-
-static void RootMatchesObjectVisitor(const mirror::Object* root, void* arg) {
+static mirror::Object* RootMatchesObjectVisitor(mirror::Object* root, void* arg) {
mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
if (root == obj) {
LOG(INFO) << "Object " << obj << " is a root";
}
+ return root;
}
class ScanVisitor {
@@ -1459,9 +1411,10 @@ class VerifyReferenceVisitor {
return heap_->IsLiveObjectLocked(obj, true, false, true);
}
- static void VerifyRoots(const mirror::Object* root, void* arg) {
+ static mirror::Object* VerifyRoots(mirror::Object* root, void* arg) {
VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
- (*visitor)(NULL, root, MemberOffset(0), true);
+ (*visitor)(nullptr, root, MemberOffset(0), true);
+ return root;
}
private:
@@ -1481,7 +1434,7 @@ class VerifyObjectVisitor {
VerifyReferenceVisitor visitor(heap_);
// The class doesn't count as a reference but we should verify it anyways.
visitor(obj, obj->GetClass(), MemberOffset(0), false);
- collector::MarkSweep::VisitObjectReferences(obj, visitor);
+ collector::MarkSweep::VisitObjectReferences(const_cast<mirror::Object*>(obj), visitor, true);
failed_ = failed_ || visitor.Failed();
}
@@ -1514,8 +1467,10 @@ bool Heap::VerifyHeapReferences() {
// pointing to dead objects if they are not reachable.
if (visitor.Failed()) {
// Dump mod-union tables.
- image_mod_union_table_->Dump(LOG(ERROR) << "Image mod-union table: ");
- zygote_mod_union_table_->Dump(LOG(ERROR) << "Zygote mod-union table: ");
+ for (const auto& table_pair : mod_union_tables_) {
+ accounting::ModUnionTable* mod_union_table = table_pair.second;
+ mod_union_table->Dump(LOG(ERROR) << mod_union_table->GetName() << ": ");
+ }
DumpSpaces();
return false;
}
@@ -1599,10 +1554,10 @@ class VerifyLiveStackReferences {
: heap_(heap),
failed_(false) {}
- void operator()(const mirror::Object* obj) const
+ void operator()(mirror::Object* obj) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
VerifyReferenceCardVisitor visitor(heap_, const_cast<bool*>(&failed_));
- collector::MarkSweep::VisitObjectReferences(obj, visitor);
+ collector::MarkSweep::VisitObjectReferences(obj, visitor, true);
}
bool Failed() const {
@@ -1638,15 +1593,23 @@ void Heap::SwapStacks() {
allocation_stack_.swap(live_stack_);
}
+accounting::ModUnionTable* Heap::FindModUnionTableFromSpace(space::Space* space) {
+ auto it = mod_union_tables_.find(space);
+ if (it == mod_union_tables_.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
void Heap::ProcessCards(base::TimingLogger& timings) {
// Clear cards and keep track of cards cleared in the mod-union table.
for (const auto& space : continuous_spaces_) {
- if (space->IsImageSpace()) {
- base::TimingLogger::ScopedSplit split("ImageModUnionClearCards", &timings);
- image_mod_union_table_->ClearCards(space);
- } else if (space->IsZygoteSpace()) {
- base::TimingLogger::ScopedSplit split("ZygoteModUnionClearCards", &timings);
- zygote_mod_union_table_->ClearCards(space);
+ accounting::ModUnionTable* table = FindModUnionTableFromSpace(space);
+ if (table != nullptr) {
+ const char* name = space->IsZygoteSpace() ? "ZygoteModUnionClearCards" :
+ "ImageModUnionClearCards";
+ base::TimingLogger::ScopedSplit split(name, &timings);
+ table->ClearCards();
} else {
base::TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings);
// No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards
@@ -1656,6 +1619,10 @@ void Heap::ProcessCards(base::TimingLogger& timings) {
}
}
+static mirror::Object* IdentityCallback(mirror::Object* obj, void*) {
+ return obj;
+}
+
void Heap::PreGcVerification(collector::GarbageCollector* gc) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* self = Thread::Current();
@@ -1689,10 +1656,11 @@ void Heap::PreGcVerification(collector::GarbageCollector* gc) {
if (verify_mod_union_table_) {
thread_list->SuspendAll();
ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_);
- zygote_mod_union_table_->Update();
- zygote_mod_union_table_->Verify();
- image_mod_union_table_->Update();
- image_mod_union_table_->Verify();
+ for (const auto& table_pair : mod_union_tables_) {
+ accounting::ModUnionTable* mod_union_table = table_pair.second;
+ mod_union_table->UpdateAndMarkReferences(IdentityCallback, nullptr);
+ mod_union_table->Verify();
+ }
thread_list->ResumeAll();
}
}
@@ -2030,8 +1998,10 @@ void Heap::RequestHeapTrim() {
// We could try mincore(2) but that's only a measure of how many pages we haven't given away,
// not how much use we're making of those pages.
uint64_t ms_time = MilliTime();
- float utilization =
- static_cast<float>(alloc_space_->GetBytesAllocated()) / alloc_space_->Size();
+ // Note the large object space's bytes allocated is equal to its capacity.
+ uint64_t los_bytes_allocated = large_object_space_->GetBytesAllocated();
+ float utilization = static_cast<float>(GetBytesAllocated() - los_bytes_allocated) /
+ (GetTotalMemory() - los_bytes_allocated);
if ((utilization > 0.75f && !IsLowMemoryMode()) || ((ms_time - last_trim_time_ms_) < 2 * 1000)) {
// Don't bother trimming the alloc space if it's more than 75% utilized and low memory mode is
// not enabled, or if a heap trim occurred in the last two seconds.
@@ -2073,24 +2043,22 @@ bool Heap::IsGCRequestPending() const {
return concurrent_start_bytes_ != std::numeric_limits<size_t>::max();
}
-void Heap::RegisterNativeAllocation(int bytes) {
+void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) {
// Total number of native bytes allocated.
native_bytes_allocated_.fetch_add(bytes);
- Thread* self = Thread::Current();
if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_gc_watermark_) {
// The second watermark is higher than the gc watermark. If you hit this it means you are
// allocating native objects faster than the GC can keep up with.
if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) {
- JNIEnv* env = self->GetJniEnv();
// Can't do this in WellKnownClasses::Init since System is not properly set up at that
// point.
- if (WellKnownClasses::java_lang_System_runFinalization == NULL) {
+ if (UNLIKELY(WellKnownClasses::java_lang_System_runFinalization == NULL)) {
DCHECK(WellKnownClasses::java_lang_System != NULL);
WellKnownClasses::java_lang_System_runFinalization =
CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V");
- assert(WellKnownClasses::java_lang_System_runFinalization != NULL);
+ CHECK(WellKnownClasses::java_lang_System_runFinalization != NULL);
}
- if (WaitForConcurrentGcToComplete(self) != collector::kGcTypeNone) {
+ if (WaitForConcurrentGcToComplete(ThreadForEnv(env)) != collector::kGcTypeNone) {
// Just finished a GC, attempt to run finalizers.
env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
WellKnownClasses::java_lang_System_runFinalization);
@@ -2109,20 +2077,22 @@ void Heap::RegisterNativeAllocation(int bytes) {
UpdateMaxNativeFootprint();
} else {
if (!IsGCRequestPending()) {
- RequestConcurrentGC(self);
+ RequestConcurrentGC(ThreadForEnv(env));
}
}
}
}
-void Heap::RegisterNativeFree(int bytes) {
+void Heap::RegisterNativeFree(JNIEnv* env, int bytes) {
int expected_size, new_size;
do {
expected_size = native_bytes_allocated_.load();
new_size = expected_size - bytes;
- if (new_size < 0) {
- ThrowRuntimeException("attempted to free %d native bytes with only %d native bytes registered as allocated",
- bytes, expected_size);
+ if (UNLIKELY(new_size < 0)) {
+ ScopedObjectAccess soa(env);
+ env->ThrowNew(WellKnownClasses::java_lang_RuntimeException,
+ StringPrintf("Attempted to free %d native bytes with only %d native bytes "
+ "registered as allocated", bytes, expected_size).c_str());
break;
}
} while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size));
@@ -2146,5 +2116,10 @@ int64_t Heap::GetTotalMemory() const {
return ret;
}
+void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) {
+ DCHECK(mod_union_table != nullptr);
+ mod_union_tables_.Put(mod_union_table->GetSpace(), mod_union_table);
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 0b64261fa1..7d2441baa1 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -101,6 +101,11 @@ enum HeapVerificationMode {
};
static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification;
+// If true, measure the total allocation time.
+static constexpr bool kMeasureAllocationTime = false;
+// Primitive arrays larger than this size are put in the large object space.
+static constexpr size_t kLargeObjectThreshold = 3 * kPageSize;
+
class Heap {
public:
static constexpr size_t kDefaultInitialSize = 2 * MB;
@@ -129,11 +134,20 @@ class Heap {
// Allocates and initializes storage for an object instance.
mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocObjectInstrumented(self, klass, num_bytes);
+ }
+ mirror::Object* AllocObjectInstrumented(Thread* self, mirror::Class* klass, size_t num_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* AllocObjectUninstrumented(Thread* self, mirror::Class* klass, size_t num_bytes)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RegisterNativeAllocation(int bytes)
+ void DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void RegisterNativeFree(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation);
+
+ void RegisterNativeAllocation(JNIEnv* env, int bytes);
+ void RegisterNativeFree(JNIEnv* env, int bytes);
// The given reference is believed to be to an object in the Java heap, check the soundness of it.
void VerifyObjectImpl(const mirror::Object* o);
@@ -368,11 +382,6 @@ class Heap {
accounting::ObjectStack* stack)
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
- // Update and mark mod union table based on gc type.
- void UpdateAndMarkModUnion(collector::MarkSweep* mark_sweep, base::TimingLogger& timings,
- collector::GcType gc_type)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
-
// Gets called when we get notified by ActivityThread that the process state has changed.
void ListenForProcessStateChange();
@@ -426,11 +435,28 @@ class Heap {
size_t GetConcGCThreadCount() const {
return conc_gc_threads_;
}
+ accounting::ModUnionTable* FindModUnionTableFromSpace(space::Space* space);
+ void AddModUnionTable(accounting::ModUnionTable* mod_union_table);
private:
+ bool TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count,
+ mirror::Object** obj_ptr, size_t* bytes_allocated)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count);
+ void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, mirror::Object* obj);
+
// Allocates uninitialized storage. Passing in a null space tries to place the object in the
// large object space.
- template <class T> mirror::Object* Allocate(Thread* self, T* space, size_t num_bytes, size_t* bytes_allocated)
+ template <class T> mirror::Object* AllocateInstrumented(Thread* self, T* space, size_t num_bytes,
+ size_t* bytes_allocated)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <class T> mirror::Object* AllocateUninstrumented(Thread* self, T* space, size_t num_bytes,
+ size_t* bytes_allocated)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -442,17 +468,29 @@ class Heap {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Try to allocate a number of bytes, this function never does any GCs.
- mirror::Object* TryToAllocate(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow,
- size_t* bytes_allocated)
+ mirror::Object* TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Try to allocate a number of bytes, this function never does any GCs. DlMallocSpace-specialized version.
- mirror::Object* TryToAllocate(Thread* self, space::DlMallocSpace* space, size_t alloc_size, bool grow,
- size_t* bytes_allocated)
+ mirror::Object* TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Object* TryToAllocateUninstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Object* TryToAllocateUninstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size,
+ bool grow, size_t* bytes_allocated)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsOutOfMemoryOnAllocation(size_t alloc_size, bool grow);
// Pushes a list of cleared references out to the managed heap.
@@ -462,7 +500,11 @@ class Heap {
void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_);
bool IsGCRequestPending() const;
- void RecordAllocation(size_t size, mirror::Object* object)
+ size_t RecordAllocationInstrumented(size_t size, mirror::Object* object)
+ LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ size_t RecordAllocationUninstrumented(size_t size, mirror::Object* object)
LOCKS_EXCLUDED(GlobalSynchronization::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -476,8 +518,9 @@ class Heap {
void PreGcVerification(collector::GarbageCollector* gc);
void PreSweepingGcVerification(collector::GarbageCollector* gc)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void PostGcVerification(collector::GarbageCollector* gc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void PostGcVerification(collector::GarbageCollector* gc);
// Update the watermark for the native allocated bytes based on the current number of native
// bytes allocated and the target utilization ratio.
@@ -522,12 +565,8 @@ class Heap {
// The card table, dirtied by the write barrier.
UniquePtr<accounting::CardTable> card_table_;
- // The mod-union table remembers all of the references from the image space to the alloc /
- // zygote spaces to allow the card table to be cleared.
- UniquePtr<accounting::ModUnionTable> image_mod_union_table_;
-
- // This table holds all of the references from the zygote space to the alloc space.
- UniquePtr<accounting::ModUnionTable> zygote_mod_union_table_;
+ // A mod-union table remembers all of the references from the it's space to other spaces.
+ SafeMap<space::Space*, accounting::ModUnionTable*> mod_union_tables_;
// What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC,
// false for stop-the-world mark sweep.
@@ -615,9 +654,6 @@ class Heap {
// Since the heap was created, how many objects have been freed.
size_t total_objects_freed_ever_;
- // Primitive objects larger than this size are put in the large object space.
- const size_t large_object_threshold_;
-
// Number of bytes allocated. Adjusted after each allocation and free.
AtomicInteger num_bytes_allocated_;
@@ -719,6 +755,16 @@ class Heap {
friend class ScopedHeapLock;
friend class space::SpaceTest;
+ class AllocationTimer {
+ private:
+ Heap* heap_;
+ mirror::Object** allocated_obj_ptr_;
+ uint64_t allocation_start_time_;
+ public:
+ AllocationTimer(Heap* heap, mirror::Object** allocated_obj_ptr);
+ ~AllocationTimer();
+ };
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Heap);
};
diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h
index 54811414e9..fb2c66b2fb 100644
--- a/runtime/gc/space/dlmalloc_space-inl.h
+++ b/runtime/gc/space/dlmalloc_space-inl.h
@@ -30,7 +30,7 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b
MutexLock mu(self, lock_);
obj = AllocWithoutGrowthLocked(num_bytes, bytes_allocated);
}
- if (obj != NULL) {
+ if (LIKELY(obj != NULL)) {
// Zero freshly allocated memory, done while not holding the space's lock.
memset(obj, 0, num_bytes);
}
@@ -39,7 +39,7 @@ inline mirror::Object* DlMallocSpace::AllocNonvirtual(Thread* self, size_t num_b
inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) {
mirror::Object* result = reinterpret_cast<mirror::Object*>(mspace_malloc(mspace_, num_bytes));
- if (result != NULL) {
+ if (LIKELY(result != NULL)) {
if (kDebugSpaces) {
CHECK(Contains(result)) << "Allocation (" << reinterpret_cast<void*>(result)
<< ") not in bounds of allocation space " << *this;
@@ -47,10 +47,6 @@ inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(size_t num_bytes,
size_t allocation_size = AllocationSizeNonvirtual(result);
DCHECK(bytes_allocated != NULL);
*bytes_allocated = allocation_size;
- num_bytes_allocated_ += allocation_size;
- total_bytes_allocated_ += allocation_size;
- ++total_objects_allocated_;
- ++num_objects_allocated_;
}
return result;
}
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index a9440d3c1b..9ebc16a4a3 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -23,7 +23,7 @@
#include "utils.h"
#include <valgrind.h>
-#include <../memcheck/memcheck.h>
+#include <memcheck/memcheck.h>
namespace art {
namespace gc {
@@ -119,8 +119,7 @@ size_t DlMallocSpace::bitmap_index_ = 0;
DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
byte* end, size_t growth_limit)
: MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect),
- recent_free_pos_(0), num_bytes_allocated_(0), num_objects_allocated_(0),
- total_bytes_allocated_(0), total_objects_allocated_(0),
+ recent_free_pos_(0), total_bytes_freed_(0), total_objects_freed_(0),
lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace),
growth_limit_(growth_limit) {
CHECK(mspace != NULL);
@@ -184,11 +183,12 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz
growth_limit = RoundUp(growth_limit, kPageSize);
capacity = RoundUp(capacity, kPageSize);
+ std::string error_msg;
UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity,
- PROT_READ | PROT_WRITE));
+ PROT_READ | PROT_WRITE, &error_msg));
if (mem_map.get() == NULL) {
LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
- << PrettySize(capacity);
+ << PrettySize(capacity) << ": " << error_msg;
return NULL;
}
@@ -287,8 +287,6 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) {
size_t size = RoundUp(Size(), kPageSize);
// Trim the heap so that we minimize the size of the Zygote space.
Trim();
- // Trim our mem-map to free unused pages.
- GetMemMap()->UnMapAtEnd(end_);
// TODO: Not hardcode these in?
const size_t starting_size = kPageSize;
const size_t initial_size = 2 * MB;
@@ -308,7 +306,11 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) {
VLOG(heap) << "Size " << GetMemMap()->Size();
VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit);
VLOG(heap) << "Capacity " << PrettySize(capacity);
- UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(alloc_space_name, End(), capacity, PROT_READ | PROT_WRITE));
+ // Remap the tail.
+ std::string error_msg;
+ UniquePtr<MemMap> mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name,
+ PROT_READ | PROT_WRITE, &error_msg));
+ CHECK(mem_map.get() != nullptr) << error_msg;
void* mspace = CreateMallocSpace(end_, starting_size, initial_size);
// Protect memory beyond the initial size.
byte* end = mem_map->Begin() + starting_size;
@@ -353,8 +355,8 @@ size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) {
CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this;
}
const size_t bytes_freed = InternalAllocationSize(ptr);
- num_bytes_allocated_ -= bytes_freed;
- --num_objects_allocated_;
+ total_bytes_freed_ += bytes_freed;
+ ++total_objects_freed_;
if (kRecentFreeCount > 0) {
RegisterRecentFree(ptr);
}
@@ -400,8 +402,8 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p
{
MutexLock mu(self, lock_);
- num_bytes_allocated_ -= bytes_freed;
- num_objects_allocated_ -= num_ptrs;
+ total_bytes_freed_ += bytes_freed;
+ total_objects_freed_ += num_ptrs;
mspace_bulk_free(mspace_, reinterpret_cast<void**>(ptrs), num_ptrs);
return bytes_freed;
}
@@ -501,6 +503,20 @@ void DlMallocSpace::Dump(std::ostream& os) const {
<< ",name=\"" << GetName() << "\"]";
}
+uint64_t DlMallocSpace::GetBytesAllocated() {
+ MutexLock mu(Thread::Current(), lock_);
+ size_t bytes_allocated = 0;
+ mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated);
+ return bytes_allocated;
+}
+
+uint64_t DlMallocSpace::GetObjectsAllocated() {
+ MutexLock mu(Thread::Current(), lock_);
+ size_t objects_allocated = 0;
+ mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated);
+ return objects_allocated;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index b08da0146b..522535e3c0 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -127,20 +127,13 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace {
// Turn ourself into a zygote space and return a new alloc space which has our unused memory.
DlMallocSpace* CreateZygoteSpace(const char* alloc_space_name);
- uint64_t GetBytesAllocated() const {
- return num_bytes_allocated_;
+ uint64_t GetBytesAllocated();
+ uint64_t GetObjectsAllocated();
+ uint64_t GetTotalBytesAllocated() {
+ return GetBytesAllocated() + total_bytes_freed_;
}
-
- uint64_t GetObjectsAllocated() const {
- return num_objects_allocated_;
- }
-
- uint64_t GetTotalBytesAllocated() const {
- return total_bytes_allocated_;
- }
-
- uint64_t GetTotalObjectsAllocated() const {
- return total_objects_allocated_;
+ uint64_t GetTotalObjectsAllocated() {
+ return GetObjectsAllocated() + total_objects_freed_;
}
// Returns the class of a recently freed object.
@@ -168,11 +161,9 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace {
std::pair<const mirror::Object*, mirror::Class*> recent_freed_objects_[kRecentFreeCount];
size_t recent_free_pos_;
- // Approximate number of bytes which have been allocated into the space.
- size_t num_bytes_allocated_;
- size_t num_objects_allocated_;
- size_t total_bytes_allocated_;
- size_t total_objects_allocated_;
+ // Approximate number of bytes and objects which have been deallocated in the space.
+ size_t total_bytes_freed_;
+ size_t total_objects_freed_;
static size_t bitmap_index_;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1cd33ee840..e12ee063c0 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -44,12 +44,13 @@ ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map,
live_bitmap_.reset(live_bitmap);
}
-static bool GenerateImage(const std::string& image_file_name) {
+static bool GenerateImage(const std::string& image_file_name, std::string* error_msg) {
const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
std::vector<std::string> boot_class_path;
Split(boot_class_path_string, ':', boot_class_path);
if (boot_class_path.empty()) {
- LOG(FATAL) << "Failed to generate image because no boot class path specified";
+ *error_msg = "Failed to generate image because no boot class path specified";
+ return false;
}
std::vector<std::string> arg_vector;
@@ -112,41 +113,57 @@ static bool GenerateImage(const std::string& image_file_name) {
return false;
} else {
if (pid == -1) {
- PLOG(ERROR) << "fork failed";
+ *error_msg = StringPrintf("Failed to generate image '%s' because fork failed: %s",
+ image_file_name.c_str(), strerror(errno));
+ return false;
}
// wait for dex2oat to finish
int status;
pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (got_pid != pid) {
- PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
+ *error_msg = StringPrintf("Failed to generate image '%s' because waitpid failed: "
+ "wanted %d, got %d: %s",
+ image_file_name.c_str(), pid, got_pid, strerror(errno));
return false;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOG(ERROR) << dex2oat << " failed: " << command_line;
+ *error_msg = StringPrintf("Failed to generate image '%s' because dex2oat failed: %s",
+ image_file_name.c_str(), command_line.c_str());
return false;
}
}
return true;
}
-ImageSpace* ImageSpace::Create(const std::string& original_image_file_name) {
- if (OS::FileExists(original_image_file_name.c_str())) {
+ImageSpace* ImageSpace::Create(const char* original_image_file_name) {
+ if (OS::FileExists(original_image_file_name)) {
// If the /system file exists, it should be up-to-date, don't try to generate
- return space::ImageSpace::Init(original_image_file_name, false);
+ std::string error_msg;
+ ImageSpace* space = ImageSpace::Init(original_image_file_name, false, &error_msg);
+ if (space == nullptr) {
+ LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg;
+ }
+ return space;
}
// If the /system file didn't exist, we need to use one from the dalvik-cache.
// If the cache file exists, try to open, but if it fails, regenerate.
// If it does not exist, generate.
std::string image_file_name(GetDalvikCacheFilenameOrDie(original_image_file_name));
+ std::string error_msg;
if (OS::FileExists(image_file_name.c_str())) {
- space::ImageSpace* image_space = space::ImageSpace::Init(image_file_name, true);
- if (image_space != NULL) {
+ space::ImageSpace* image_space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg);
+ if (image_space != nullptr) {
return image_space;
}
}
- CHECK(GenerateImage(image_file_name)) << "Failed to generate image: " << image_file_name;
- return space::ImageSpace::Init(image_file_name, true);
+ CHECK(GenerateImage(image_file_name, &error_msg))
+ << "Failed to generate image '" << image_file_name << "': " << error_msg;
+ ImageSpace* space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg);
+ if (space == nullptr) {
+ LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg;
+ }
+ return space;
}
void ImageSpace::VerifyImageAllocations() {
@@ -160,8 +177,9 @@ void ImageSpace::VerifyImageAllocations() {
}
}
-ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_oat_file) {
- CHECK(!image_file_name.empty());
+ImageSpace* ImageSpace::Init(const char* image_file_name, bool validate_oat_file,
+ std::string* error_msg) {
+ CHECK(image_file_name != nullptr);
uint64_t start_time = 0;
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -169,16 +187,16 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o
LOG(INFO) << "ImageSpace::Init entering image_file_name=" << image_file_name;
}
- UniquePtr<File> file(OS::OpenFileForReading(image_file_name.c_str()));
+ UniquePtr<File> file(OS::OpenFileForReading(image_file_name));
if (file.get() == NULL) {
- LOG(ERROR) << "Failed to open " << image_file_name;
- return NULL;
+ *error_msg = StringPrintf("Failed to open '%s'", image_file_name);
+ return nullptr;
}
ImageHeader image_header;
bool success = file->ReadFully(&image_header, sizeof(image_header));
if (!success || !image_header.IsValid()) {
- LOG(ERROR) << "Invalid image header " << image_file_name;
- return NULL;
+ *error_msg = StringPrintf("Invalid image header in '%s'", image_file_name);
+ return nullptr;
}
// Note: The image header is part of the image due to mmap page alignment required of offset.
@@ -188,10 +206,12 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o
MAP_PRIVATE | MAP_FIXED,
file->Fd(),
0,
- false));
+ false,
+ image_file_name,
+ error_msg));
if (map.get() == NULL) {
- LOG(ERROR) << "Failed to map " << image_file_name;
- return NULL;
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
CHECK_EQ(image_header.GetImageBegin(), map->Begin());
DCHECK_EQ(0, memcmp(&image_header, map->Begin(), sizeof(ImageHeader)));
@@ -199,20 +219,32 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o
UniquePtr<MemMap> image_map(MemMap::MapFileAtAddress(nullptr, image_header.GetImageBitmapSize(),
PROT_READ, MAP_PRIVATE,
file->Fd(), image_header.GetBitmapOffset(),
- false));
- CHECK(image_map.get() != nullptr) << "failed to map image bitmap";
+ false,
+ image_file_name,
+ error_msg));
+ if (image_map.get() == nullptr) {
+ *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
+ return nullptr;
+ }
size_t bitmap_index = bitmap_index_.fetch_add(1);
- std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name.c_str(),
+ std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name,
bitmap_index));
UniquePtr<accounting::SpaceBitmap> bitmap(
accounting::SpaceBitmap::CreateFromMemMap(bitmap_name, image_map.release(),
reinterpret_cast<byte*>(map->Begin()),
map->Size()));
- CHECK(bitmap.get() != nullptr) << "could not create " << bitmap_name;
+ if (bitmap.get() == nullptr) {
+ *error_msg = StringPrintf("Could not create bitmap '%s'", bitmap_name.c_str());
+ return nullptr;
+ }
Runtime* runtime = Runtime::Current();
mirror::Object* resolution_method = image_header.GetImageRoot(ImageHeader::kResolutionMethod);
runtime->SetResolutionMethod(down_cast<mirror::ArtMethod*>(resolution_method));
+ mirror::Object* imt_conflict_method = image_header.GetImageRoot(ImageHeader::kImtConflictMethod);
+ runtime->SetImtConflictMethod(down_cast<mirror::ArtMethod*>(imt_conflict_method));
+ mirror::Object* default_imt = image_header.GetImageRoot(ImageHeader::kDefaultImt);
+ runtime->SetDefaultImt(down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(default_imt));
mirror::Object* callee_save_method = image_header.GetImageRoot(ImageHeader::kCalleeSaveMethod);
runtime->SetCalleeSaveMethod(down_cast<mirror::ArtMethod*>(callee_save_method), Runtime::kSaveAll);
@@ -226,15 +258,15 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o
space->VerifyImageAllocations();
}
- space->oat_file_.reset(space->OpenOatFile());
- if (space->oat_file_.get() == NULL) {
- LOG(ERROR) << "Failed to open oat file for image: " << image_file_name;
- return NULL;
+ space->oat_file_.reset(space->OpenOatFile(error_msg));
+ if (space->oat_file_.get() == nullptr) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
- if (validate_oat_file && !space->ValidateOatFile()) {
- LOG(WARNING) << "Failed to validate oat file for image: " << image_file_name;
- return NULL;
+ if (validate_oat_file && !space->ValidateOatFile(error_msg)) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
}
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
@@ -244,7 +276,7 @@ ImageSpace* ImageSpace::Init(const std::string& image_file_name, bool validate_o
return space.release();
}
-OatFile* ImageSpace::OpenOatFile() const {
+OatFile* ImageSpace::OpenOatFile(std::string* error_msg) const {
const Runtime* runtime = Runtime::Current();
const ImageHeader& image_header = GetImageHeader();
// Grab location but don't use Object::AsString as we haven't yet initialized the roots to
@@ -255,45 +287,47 @@ OatFile* ImageSpace::OpenOatFile() const {
oat_filename += runtime->GetHostPrefix();
oat_filename += oat_location->ToModifiedUtf8();
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
- !Runtime::Current()->IsCompiler());
+ !Runtime::Current()->IsCompiler(), error_msg);
if (oat_file == NULL) {
- LOG(ERROR) << "Failed to open oat file " << oat_filename << " referenced from image.";
- return NULL;
+ *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
+ oat_filename.c_str(), GetName(), error_msg->c_str());
+ return nullptr;
}
uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
uint32_t image_oat_checksum = image_header.GetOatChecksum();
if (oat_checksum != image_oat_checksum) {
- LOG(ERROR) << "Failed to match oat file checksum " << std::hex << oat_checksum
- << " to expected oat checksum " << std::hex << image_oat_checksum
- << " in image";
- return NULL;
+ *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x"
+ " in image %s", oat_checksum, image_oat_checksum, GetName());
+ return nullptr;
}
return oat_file;
}
-bool ImageSpace::ValidateOatFile() const {
+bool ImageSpace::ValidateOatFile(std::string* error_msg) const {
CHECK(oat_file_.get() != NULL);
for (const OatFile::OatDexFile* oat_dex_file : oat_file_->GetOatDexFiles()) {
const std::string& dex_file_location = oat_dex_file->GetDexFileLocation();
uint32_t dex_file_location_checksum;
- if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum)) {
- LOG(WARNING) << "ValidateOatFile could not find checksum for " << dex_file_location;
+ if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) {
+ *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: "
+ "%s", dex_file_location.c_str(), GetName(), error_msg->c_str());
return false;
}
if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
- LOG(WARNING) << "ValidateOatFile found checksum mismatch between oat file "
- << oat_file_->GetLocation() << " and dex file " << dex_file_location
- << " (" << oat_dex_file->GetDexFileLocationChecksum() << " != "
- << dex_file_location_checksum << ")";
+ *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and "
+ "dex file '%s' (0x%x != 0x%x)",
+ oat_file_->GetLocation().c_str(), dex_file_location.c_str(),
+ oat_dex_file->GetDexFileLocationChecksum(),
+ dex_file_location_checksum);
return false;
}
}
return true;
}
-OatFile& ImageSpace::ReleaseOatFile() {
+OatFile* ImageSpace::ReleaseOatFile() {
CHECK(oat_file_.get() != NULL);
- return *oat_file_.release();
+ return oat_file_.release();
}
void ImageSpace::Dump(std::ostream& os) const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 381a98e386..78a83c92cd 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -45,12 +45,11 @@ class ImageSpace : public MemMapSpace {
// creation of the alloc space. The ReleaseOatFile will later be
// used to transfer ownership of the OatFile to the ClassLinker when
// it is initialized.
- static ImageSpace* Create(const std::string& image)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static ImageSpace* Create(const char* image) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Releases the OatFile from the ImageSpace so it can be transfer to
// the caller, presumably the ClassLinker.
- OatFile& ReleaseOatFile()
+ OatFile* ReleaseOatFile()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void VerifyImageAllocations()
@@ -84,13 +83,13 @@ class ImageSpace : public MemMapSpace {
// image's OatFile is up-to-date relative to its DexFile
// inputs. Otherwise (for /data), validate the inputs and generate
// the OatFile in /data/dalvik-cache if necessary.
- static ImageSpace* Init(const std::string& image, bool validate_oat_file)
+ static ImageSpace* Init(const char* image, bool validate_oat_file, std::string* error_msg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- OatFile* OpenOatFile() const
+ OatFile* OpenOatFile(std::string* error_msg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool ValidateOatFile() const
+ bool ValidateOatFile(std::string* error_msg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
friend class Space;
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index a174c0a1dc..1321b193aa 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -17,6 +17,7 @@
#include "large_object_space.h"
#include "base/logging.h"
+#include "base/mutex-inl.h"
#include "base/stl_util.h"
#include "UniquePtr.h"
#include "image.h"
@@ -55,10 +56,13 @@ LargeObjectMapSpace* LargeObjectMapSpace::Create(const std::string& name) {
return new LargeObjectMapSpace(name);
}
-mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated) {
+mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes,
+ size_t* bytes_allocated) {
+ std::string error_msg;
MemMap* mem_map = MemMap::MapAnonymous("large object space allocation", NULL, num_bytes,
- PROT_READ | PROT_WRITE);
- if (mem_map == NULL) {
+ PROT_READ | PROT_WRITE, &error_msg);
+ if (UNLIKELY(mem_map == NULL)) {
+ LOG(WARNING) << "Large object allocation failed: " << error_msg;
return NULL;
}
MutexLock mu(self, lock_);
@@ -128,9 +132,10 @@ bool LargeObjectMapSpace::Contains(const mirror::Object* obj) const {
FreeListSpace* FreeListSpace::Create(const std::string& name, byte* requested_begin, size_t size) {
CHECK_EQ(size % kAlignment, 0U);
+ std::string error_msg;
MemMap* mem_map = MemMap::MapAnonymous(name.c_str(), requested_begin, size,
- PROT_READ | PROT_WRITE);
- CHECK(mem_map != NULL) << "Failed to allocate large object space mem map";
+ PROT_READ | PROT_WRITE, &error_msg);
+ CHECK(mem_map != NULL) << "Failed to allocate large object space mem map: " << error_msg;
return new FreeListSpace(name, mem_map, mem_map->Begin(), mem_map->End());
}
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index a703e86ea1..ef889d42c2 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -41,19 +41,19 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace {
virtual void Walk(DlMallocSpace::WalkCallback, void* arg) = 0;
virtual ~LargeObjectSpace() {}
- uint64_t GetBytesAllocated() const {
+ uint64_t GetBytesAllocated() {
return num_bytes_allocated_;
}
- uint64_t GetObjectsAllocated() const {
+ uint64_t GetObjectsAllocated() {
return num_objects_allocated_;
}
- uint64_t GetTotalBytesAllocated() const {
+ uint64_t GetTotalBytesAllocated() {
return total_bytes_allocated_;
}
- uint64_t GetTotalObjectsAllocated() const {
+ uint64_t GetTotalObjectsAllocated() {
return total_objects_allocated_;
}
@@ -96,9 +96,9 @@ class LargeObjectMapSpace : public LargeObjectSpace {
// Used to ensure mutual exclusion when the allocation spaces data structures are being modified.
mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<mirror::Object*,
- accounting::GCAllocator<mirror::Object*> > large_objects_ GUARDED_BY(lock_);
+ accounting::GcAllocator<mirror::Object*> > large_objects_ GUARDED_BY(lock_);
typedef SafeMap<mirror::Object*, MemMap*, std::less<mirror::Object*>,
- accounting::GCAllocator<std::pair<const mirror::Object*, MemMap*> > > MemMaps;
+ accounting::GcAllocator<std::pair<const mirror::Object*, MemMap*> > > MemMaps;
MemMaps mem_maps_ GUARDED_BY(lock_);
};
@@ -217,7 +217,7 @@ class FreeListSpace : public LargeObjectSpace {
AllocationHeader* GetAllocationHeader(const mirror::Object* obj);
typedef std::set<AllocationHeader*, AllocationHeader::SortByPrevFree,
- accounting::GCAllocator<AllocationHeader*> > FreeBlocks;
+ accounting::GcAllocator<AllocationHeader*> > FreeBlocks;
byte* const begin_;
byte* const end_;
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 68df56333f..6dd795227d 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -146,13 +146,13 @@ std::ostream& operator<<(std::ostream& os, const Space& space);
class AllocSpace {
public:
// Number of bytes currently allocated.
- virtual uint64_t GetBytesAllocated() const = 0;
+ virtual uint64_t GetBytesAllocated() = 0;
// Number of objects currently allocated.
- virtual uint64_t GetObjectsAllocated() const = 0;
+ virtual uint64_t GetObjectsAllocated() = 0;
// Number of bytes allocated since the space was created.
- virtual uint64_t GetTotalBytesAllocated() const = 0;
+ virtual uint64_t GetTotalBytesAllocated() = 0;
// Number of objects allocated since the space was created.
- virtual uint64_t GetTotalObjectsAllocated() const = 0;
+ virtual uint64_t GetTotalObjectsAllocated() = 0;
// Allocate num_bytes without allowing growth. If the allocation
// succeeds, the output parameter bytes_allocated will be set to the
diff --git a/runtime/globals.h b/runtime/globals.h
index c3974943cd..31574ff72d 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -30,30 +30,30 @@ const size_t KB = 1024;
const size_t MB = KB * KB;
const size_t GB = KB * KB * KB;
-const int kWordSize = sizeof(word);
-const int kPointerSize = sizeof(void*);
+const size_t kWordSize = sizeof(word);
+const size_t kPointerSize = sizeof(void*);
-const int kBitsPerByte = 8;
-const int kBitsPerByteLog2 = 3;
+const size_t kBitsPerByte = 8;
+const size_t kBitsPerByteLog2 = 3;
const int kBitsPerWord = kWordSize * kBitsPerByte;
-const int kWordHighBitMask = 1 << (kBitsPerWord - 1);
+const size_t kWordHighBitMask = 1 << (kBitsPerWord - 1);
// Required stack alignment
-const int kStackAlignment = 16;
+const size_t kStackAlignment = 16;
// Required object alignment
-const int kObjectAlignment = 8;
+const size_t kObjectAlignment = 8;
// ARM instruction alignment. ARM processors require code to be 4-byte aligned,
// but ARM ELF requires 8..
-const int kArmAlignment = 8;
+const size_t kArmAlignment = 8;
// MIPS instruction alignment. MIPS processors require code to be 4-byte aligned.
// TODO: Can this be 4?
-const int kMipsAlignment = 8;
+const size_t kMipsAlignment = 8;
// X86 instruction alignment. This is the recommended alignment for maximum performance.
-const int kX86Alignment = 16;
+const size_t kX86Alignment = 16;
// System page size. We check this against sysconf(_SC_PAGE_SIZE) at runtime, but use a simple
// compile-time constant so the compiler can generate better code.
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 0b2e741527..67620a09f1 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -484,11 +484,11 @@ class Hprof {
}
private:
- static void RootVisitor(const mirror::Object* obj, void* arg)
+ static mirror::Object* RootVisitor(mirror::Object* obj, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- CHECK(arg != NULL);
- Hprof* hprof = reinterpret_cast<Hprof*>(arg);
- hprof->VisitRoot(obj);
+ DCHECK(arg != NULL);
+ reinterpret_cast<Hprof*>(arg)->VisitRoot(obj);
+ return obj;
}
static void HeapBitmapCallback(mirror::Object* obj, void* arg)
diff --git a/runtime/image.h b/runtime/image.h
index 2cb468fed1..246f106382 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -90,6 +90,8 @@ class PACKED(4) ImageHeader {
enum ImageRoot {
kResolutionMethod,
+ kImtConflictMethod,
+ kDefaultImt,
kCalleeSaveMethod,
kRefsOnlySaveMethod,
kRefsAndArgsSaveMethod,
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 8af4d7eaab..2bd83538ac 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -40,7 +40,7 @@ IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
CHECK_LE(initialCount, maxCount);
CHECK_NE(desiredKind, kSirtOrInvalid);
- table_ = reinterpret_cast<const mirror::Object**>(malloc(initialCount * sizeof(const mirror::Object*)));
+ table_ = reinterpret_cast<mirror::Object**>(malloc(initialCount * sizeof(const mirror::Object*)));
CHECK(table_ != NULL);
memset(table_, 0xd1, initialCount * sizeof(const mirror::Object*));
@@ -75,7 +75,7 @@ bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int
return true;
}
-IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* obj) {
+IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) {
IRTSegmentState prevState;
prevState.all = cookie;
size_t topIndex = segment_state_.parts.topIndex;
@@ -101,7 +101,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* o
}
DCHECK_GT(newSize, alloc_entries_);
- table_ = reinterpret_cast<const mirror::Object**>(realloc(table_, newSize * sizeof(const mirror::Object*)));
+ table_ = reinterpret_cast<mirror::Object**>(realloc(table_, newSize * sizeof(mirror::Object*)));
slot_data_ = reinterpret_cast<IndirectRefSlot*>(realloc(slot_data_,
newSize * sizeof(IndirectRefSlot)));
if (table_ == NULL || slot_data_ == NULL) {
@@ -126,7 +126,7 @@ IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* o
if (numHoles > 0) {
DCHECK_GT(topIndex, 1U);
// Find the first hole; likely to be near the end of the list.
- const mirror::Object** pScan = &table_[topIndex - 1];
+ mirror::Object** pScan = &table_[topIndex - 1];
DCHECK(*pScan != NULL);
while (*--pScan != NULL) {
DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
@@ -194,7 +194,8 @@ bool IndirectReferenceTable::GetChecked(IndirectRef iref) const {
return true;
}
-static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex, const mirror::Object** table) {
+static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex,
+ mirror::Object** table) {
for (int i = bottomIndex; i < topIndex; ++i) {
if (table[i] == direct_pointer) {
return i;
@@ -310,13 +311,14 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) {
void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) {
for (auto ref : *this) {
- visitor(*ref, arg);
+ *ref = visitor(const_cast<mirror::Object*>(*ref), arg);
+ DCHECK(*ref != nullptr);
}
}
void IndirectReferenceTable::Dump(std::ostream& os) const {
os << kind_ << " table dump:\n";
- std::vector<const mirror::Object*> entries(table_, table_ + Capacity());
+ ReferenceTable::Table entries(table_, table_ + Capacity());
// Remove NULLs.
for (int i = entries.size() - 1; i >= 0; --i) {
if (entries[i] == NULL) {
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index 26f53db4ad..51b238c527 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -206,7 +206,7 @@ union IRTSegmentState {
class IrtIterator {
public:
- explicit IrtIterator(const mirror::Object** table, size_t i, size_t capacity)
+ explicit IrtIterator(mirror::Object** table, size_t i, size_t capacity)
: table_(table), i_(i), capacity_(capacity) {
SkipNullsAndTombstones();
}
@@ -217,7 +217,7 @@ class IrtIterator {
return *this;
}
- const mirror::Object** operator*() {
+ mirror::Object** operator*() {
return &table_[i_];
}
@@ -233,7 +233,7 @@ class IrtIterator {
}
}
- const mirror::Object** table_;
+ mirror::Object** table_;
size_t i_;
size_t capacity_;
};
@@ -258,7 +258,7 @@ class IndirectReferenceTable {
* Returns NULL if the table is full (max entries reached, or alloc
* failed during expansion).
*/
- IndirectRef Add(uint32_t cookie, const mirror::Object* obj)
+ IndirectRef Add(uint32_t cookie, mirror::Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
@@ -266,7 +266,7 @@ class IndirectReferenceTable {
*
* Returns kInvalidIndirectRefObject if iref is invalid.
*/
- const mirror::Object* Get(IndirectRef iref) const {
+ mirror::Object* Get(IndirectRef iref) const {
if (!GetChecked(iref)) {
return kInvalidIndirectRefObject;
}
@@ -363,7 +363,7 @@ class IndirectReferenceTable {
IRTSegmentState segment_state_;
/* bottom of the stack */
- const mirror::Object** table_;
+ mirror::Object** table_;
/* bit mask, ORed into all irefs */
IndirectRefKind kind_;
/* extended debugging info */
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index bd2890c497..b6c6cb4be3 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -17,6 +17,7 @@
#include "common_test.h"
#include "indirect_reference_table.h"
+#include "mirror/object-inl.h"
namespace art {
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index 2217f7f76e..aee7447faf 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -18,6 +18,9 @@
#define ART_RUNTIME_INSTRUCTION_SET_H_
#include <iosfwd>
+#include <string>
+
+#include "base/macros.h"
namespace art {
@@ -29,6 +32,53 @@ enum InstructionSet {
kMips
};
+enum InstructionFeatures {
+ kHwDiv = 1 // Supports hardware divide.
+};
+
+// This is a bitmask of supported features per architecture.
+class PACKED(4) InstructionSetFeatures {
+ public:
+ InstructionSetFeatures() : mask_(0) {}
+ explicit InstructionSetFeatures(uint32_t mask) : mask_(mask) {}
+
+ bool HasDivideInstruction() const {
+ return (mask_ & kHwDiv) != 0;
+ }
+
+ void SetHasDivideInstruction(bool v) {
+ mask_ = (mask_ & ~kHwDiv) | (v ? kHwDiv : 0);
+ }
+
+ std::string GetFeatureString() const {
+ std::string result;
+ if ((mask_ & kHwDiv) != 0) {
+ result += "div";
+ }
+ if (result.size() == 0) {
+ result = "none";
+ }
+ return result;
+ }
+
+ uint32_t get_mask() const {
+ return mask_;
+ }
+
+ // Other features in here.
+
+ bool operator==(const InstructionSetFeatures &peer) const {
+ return mask_ == peer.mask_;
+ }
+
+ bool operator!=(const InstructionSetFeatures &peer) const {
+ return mask_ != peer.mask_;
+ }
+
+ private:
+ uint32_t mask_;
+};
+
std::ostream& operator<<(std::ostream& os, const InstructionSet& rhs);
} // namespace art
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 6caad0110d..8316bc56b3 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -41,6 +41,11 @@
namespace art {
namespace instrumentation {
+// Do we want to deoptimize for method entry and exit listeners or just try to intercept
+// invocations? Deoptimization forces all code to run in the interpreter and considerably hurts the
+// application's performance.
+static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = false;
+
static bool InstallStubsClassVisitor(mirror::Class* klass, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
@@ -264,12 +269,14 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev
bool require_interpreter = false;
if ((events & kMethodEntered) != 0) {
method_entry_listeners_.push_back(listener);
- require_entry_exit_stubs = true;
+ require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners;
have_method_entry_listeners_ = true;
}
if ((events & kMethodExited) != 0) {
method_exit_listeners_.push_back(listener);
- require_entry_exit_stubs = true;
+ require_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_entry_exit_stubs = !kDeoptimizeForAccurateMethodEntryExitListeners;
have_method_exit_listeners_ = true;
}
if ((events & kMethodUnwind) != 0) {
@@ -286,6 +293,7 @@ void Instrumentation::AddListener(InstrumentationListener* listener, uint32_t ev
have_exception_caught_listeners_ = true;
}
ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+ UpdateInterpreterHandlerTable();
}
void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t events) {
@@ -300,7 +308,10 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t
method_entry_listeners_.remove(listener);
}
have_method_entry_listeners_ = method_entry_listeners_.size() > 0;
- require_entry_exit_stubs |= have_method_entry_listeners_;
+ require_entry_exit_stubs |= have_method_entry_listeners_ &&
+ !kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_interpreter = have_method_entry_listeners_ &&
+ kDeoptimizeForAccurateMethodEntryExitListeners;
}
if ((events & kMethodExited) != 0) {
bool contains = std::find(method_exit_listeners_.begin(), method_exit_listeners_.end(),
@@ -309,7 +320,10 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t
method_exit_listeners_.remove(listener);
}
have_method_exit_listeners_ = method_exit_listeners_.size() > 0;
- require_entry_exit_stubs |= have_method_exit_listeners_;
+ require_entry_exit_stubs |= have_method_exit_listeners_ &&
+ !kDeoptimizeForAccurateMethodEntryExitListeners;
+ require_interpreter = have_method_exit_listeners_ &&
+ kDeoptimizeForAccurateMethodEntryExitListeners;
}
if ((events & kMethodUnwind) != 0) {
method_unwind_listeners_.remove(listener);
@@ -328,6 +342,7 @@ void Instrumentation::RemoveListener(InstrumentationListener* listener, uint32_t
have_exception_caught_listeners_ = exception_caught_listeners_.size() > 0;
}
ConfigureStubs(require_entry_exit_stubs, require_interpreter);
+ UpdateInterpreterHandlerTable();
}
void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) {
@@ -455,7 +470,7 @@ void Instrumentation::DexPcMovedEventImpl(Thread* thread, mirror::Object* this_o
void Instrumentation::ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
mirror::ArtMethod* catch_method,
uint32_t catch_dex_pc,
- mirror::Throwable* exception_object) {
+ mirror::Throwable* exception_object) const {
if (have_exception_caught_listeners_) {
DCHECK_EQ(thread->GetException(NULL), exception_object);
thread->ClearException();
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 6c80b41b64..7a0aaf7858 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -38,6 +38,14 @@ namespace instrumentation {
const bool kVerboseInstrumentation = false;
+// Interpreter handler tables.
+enum InterpreterHandlerTable {
+ kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation.
+ kAlternativeHandlerTable = 1, // Alternative handler table: suspend check and/or instrumentation
+ // enabled.
+ kNumHandlerTables
+};
+
// Instrumentation event listener API. Registered listeners will get the appropriate call back for
// the events they are listening for. The call backs supply the thread, method and dex_pc the event
// occurred upon. The thread may or may not be Thread::Current().
@@ -95,7 +103,8 @@ class Instrumentation {
interpret_only_(false), forced_interpret_only_(false),
have_method_entry_listeners_(false), have_method_exit_listeners_(false),
have_method_unwind_listeners_(false), have_dex_pc_listeners_(false),
- have_exception_caught_listeners_(false) {}
+ have_exception_caught_listeners_(false),
+ interpreter_handler_table_(kMainHandlerTable) {}
// Add a listener to be notified of the masked together sent of instrumentation events. This
// suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy
@@ -110,6 +119,10 @@ class Instrumentation {
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
+ InterpreterHandlerTable GetInterpreterHandlerTable() const {
+ return interpreter_handler_table_;
+ }
+
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const;
@@ -149,6 +162,11 @@ class Instrumentation {
return have_dex_pc_listeners_;
}
+ bool IsActive() const {
+ return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
+ have_exception_caught_listeners_ || have_method_unwind_listeners_;
+ }
+
// Inform listeners that a method has been entered. A dex PC is provided as we may install
// listeners into executing code and get method enter events for methods already on the stack.
void MethodEnterEvent(Thread* thread, mirror::Object* this_object,
@@ -186,7 +204,7 @@ class Instrumentation {
// Inform listeners that an exception was caught.
void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location,
mirror::ArtMethod* catch_method, uint32_t catch_dex_pc,
- mirror::Throwable* exception_object)
+ mirror::Throwable* exception_object) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Called when an instrumented method is entered. The intended link register (lr) is saved so
@@ -215,6 +233,10 @@ class Instrumentation {
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_);
+ void UpdateInterpreterHandlerTable() {
+ interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable;
+ }
+
void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object,
const mirror::ArtMethod* method, uint32_t dex_pc) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -267,6 +289,9 @@ class Instrumentation {
std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_);
+ // Current interpreter handler table. This is updated each time the thread state flags are modified.
+ InterpreterHandlerTable interpreter_handler_table_;
+
DISALLOW_COPY_AND_ASSIGN(Instrumentation);
};
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 20729790a9..8f9e072093 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -47,15 +47,16 @@ void InternTable::VisitRoots(RootVisitor* visitor, void* arg,
bool only_dirty, bool clean_dirty) {
MutexLock mu(Thread::Current(), intern_table_lock_);
if (!only_dirty || is_dirty_) {
- for (const auto& strong_intern : strong_interns_) {
- visitor(strong_intern.second, arg);
+ for (auto& strong_intern : strong_interns_) {
+ strong_intern.second = reinterpret_cast<mirror::String*>(visitor(strong_intern.second, arg));
+ DCHECK(strong_intern.second != nullptr);
}
+
if (clean_dirty) {
is_dirty_ = false;
}
}
- // Note: we deliberately don't visit the weak_interns_ table and the immutable
- // image roots.
+ // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots.
}
mirror::String* InternTable::Lookup(Table& table, mirror::String* s,
@@ -216,14 +217,16 @@ bool InternTable::ContainsWeak(mirror::String* s) {
return found == s;
}
-void InternTable::SweepInternTableWeaks(IsMarkedTester is_marked, void* arg) {
+void InternTable::SweepInternTableWeaks(RootVisitor visitor, void* arg) {
MutexLock mu(Thread::Current(), intern_table_lock_);
- // TODO: std::remove_if + lambda.
for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) {
mirror::Object* object = it->second;
- if (!is_marked(object, arg)) {
+ mirror::Object* new_object = visitor(object, arg);
+ if (new_object == nullptr) {
+ // TODO: use it = weak_interns_.erase(it) when we get a c++11 stl.
weak_interns_.erase(it++);
} else {
+ it->second = down_cast<mirror::String*>(new_object);
++it;
}
}
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index e68af907ea..eec63c874f 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -55,8 +55,7 @@ class InternTable {
// Interns a potentially new string in the 'weak' table. (See above.)
mirror::String* InternWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepInternTableWeaks(IsMarkedTester is_marked, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+ void SweepInternTableWeaks(RootVisitor visitor, void* arg);
bool ContainsWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index d79d2c4b31..aa2502d81f 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -81,8 +81,11 @@ class TestPredicate {
mutable std::vector<const mirror::String*> expected_;
};
-bool IsMarked(const mirror::Object* object, void* arg) {
- return reinterpret_cast<TestPredicate*>(arg)->IsMarked(object);
+mirror::Object* IsMarkedSweepingVisitor(mirror::Object* object, void* arg) {
+ if (reinterpret_cast<TestPredicate*>(arg)->IsMarked(object)) {
+ return object;
+ }
+ return nullptr;
}
TEST_F(InternTableTest, SweepInternTableWeaks) {
@@ -105,7 +108,7 @@ TEST_F(InternTableTest, SweepInternTableWeaks) {
p.Expect(s1.get());
{
ReaderMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
- t.SweepInternTableWeaks(IsMarked, &p);
+ t.SweepInternTableWeaks(IsMarkedSweepingVisitor, &p);
}
EXPECT_EQ(2U, t.Size());
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 8cd2ac8597..d7555ddb6a 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -14,153 +14,11 @@
* limitations under the License.
*/
-#include "interpreter.h"
-
-#include <math.h>
-
-#include "base/logging.h"
-#include "class_linker-inl.h"
-#include "common_throws.h"
-#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
-#include "dex_instruction.h"
-#include "entrypoints/entrypoint_utils.h"
-#include "gc/accounting/card_table-inl.h"
-#include "invoke_arg_array_builder.h"
-#include "nth_caller_visitor.h"
-#include "mirror/art_field-inl.h"
-#include "mirror/art_method.h"
-#include "mirror/art_method-inl.h"
-#include "mirror/class.h"
-#include "mirror/class-inl.h"
-#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
-#include "object_utils.h"
-#include "ScopedLocalRef.h"
-#include "scoped_thread_state_change.h"
-#include "thread.h"
-#include "well_known_classes.h"
-
-using ::art::mirror::ArtField;
-using ::art::mirror::ArtMethod;
-using ::art::mirror::Array;
-using ::art::mirror::BooleanArray;
-using ::art::mirror::ByteArray;
-using ::art::mirror::CharArray;
-using ::art::mirror::Class;
-using ::art::mirror::ClassLoader;
-using ::art::mirror::IntArray;
-using ::art::mirror::LongArray;
-using ::art::mirror::Object;
-using ::art::mirror::ObjectArray;
-using ::art::mirror::ShortArray;
-using ::art::mirror::String;
-using ::art::mirror::Throwable;
+#include "interpreter_common.h"
namespace art {
-
namespace interpreter {
-static const int32_t kMaxInt = std::numeric_limits<int32_t>::max();
-static const int32_t kMinInt = std::numeric_limits<int32_t>::min();
-static const int64_t kMaxLong = std::numeric_limits<int64_t>::max();
-static const int64_t kMinLong = std::numeric_limits<int64_t>::min();
-
-static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
- const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
- JValue* result, size_t arg_offset)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- // In a runtime that's not started we intercept certain methods to avoid complicated dependency
- // problems in core libraries.
- std::string name(PrettyMethod(shadow_frame->GetMethod()));
- if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
- std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
- ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
- Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
- class_loader);
- CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
- << PrettyDescriptor(descriptor);
- result->SetL(found);
- } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
- Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
- ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
- CHECK(c != NULL);
- SirtRef<Object> obj(self, klass->AllocObject(self));
- CHECK(obj.get() != NULL);
- EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL);
- result->SetL(obj.get());
- } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
- // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
- // going the reflective Dex way.
- Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
- String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
- ArtField* found = NULL;
- FieldHelper fh;
- ObjectArray<ArtField>* fields = klass->GetIFields();
- for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
- ArtField* f = fields->Get(i);
- fh.ChangeField(f);
- if (name->Equals(fh.GetName())) {
- found = f;
- }
- }
- if (found == NULL) {
- fields = klass->GetSFields();
- for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
- ArtField* f = fields->Get(i);
- fh.ChangeField(f);
- if (name->Equals(fh.GetName())) {
- found = f;
- }
- }
- }
- CHECK(found != NULL)
- << "Failed to find field in Class.getDeclaredField in un-started runtime. name="
- << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
- // TODO: getDeclaredField calls GetType once the field is found to ensure a
- // NoClassDefFoundError is thrown if the field's type cannot be resolved.
- Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass();
- SirtRef<Object> field(self, jlr_Field->AllocObject(self));
- CHECK(field.get() != NULL);
- ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("<init>", "(Ljava/lang/reflect/ArtField;)V");
- uint32_t args[1];
- args[0] = reinterpret_cast<uint32_t>(found);
- EnterInterpreterFromInvoke(self, c, field.get(), args, NULL);
- result->SetL(field.get());
- } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" ||
- name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") {
- // Special case array copying without initializing System.
- Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
- jint srcPos = shadow_frame->GetVReg(arg_offset + 1);
- jint dstPos = shadow_frame->GetVReg(arg_offset + 3);
- jint length = shadow_frame->GetVReg(arg_offset + 4);
- if (!ctype->IsPrimitive()) {
- ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>();
- ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>();
- for (jint i = 0; i < length; ++i) {
- dst->Set(dstPos + i, src->Get(srcPos + i));
- }
- } else if (ctype->IsPrimitiveChar()) {
- CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray();
- CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray();
- for (jint i = 0; i < length; ++i) {
- dst->Set(dstPos + i, src->Get(srcPos + i));
- }
- } else if (ctype->IsPrimitiveInt()) {
- IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray();
- IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray();
- for (jint i = 0; i < length; ++i) {
- dst->Set(dstPos + i, src->Get(srcPos + i));
- }
- } else {
- UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype);
- }
- } else {
- // Not special, continue with regular interpreter execution.
- artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result);
- }
-}
-
// Hand select a number of methods to be run in a not yet started runtime without using JNI.
static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
Object* receiver, uint32_t* args, JValue* result)
@@ -224,7 +82,7 @@ static void UnstartedRuntimeJni(Thread* self, ArtMethod* method,
}
}
-static void InterpreterJni(Thread* self, ArtMethod* method, StringPiece shorty,
+static void InterpreterJni(Thread* self, ArtMethod* method, const StringPiece& shorty,
Object* receiver, uint32_t* args, JValue* result)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// TODO: The following enters JNI code using a typedef-ed function rather than the JNI compiler,
@@ -406,2703 +264,12 @@ static void InterpreterJni(Thread* self, ArtMethod* method, StringPiece shorty,
}
}
-static void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
- ref->MonitorEnter(self);
-}
-
-static void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
- ref->MonitorExit(self);
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<InvokeType type, bool is_range, bool do_access_check>
-static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result) NO_THREAD_SAFETY_ANALYSIS;
-
-template<InvokeType type, bool is_range, bool do_access_check>
-static bool DoInvoke(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result) {
- bool do_assignability_check = do_access_check;
- uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- Object* receiver = (type == kStatic) ? NULL : shadow_frame.GetVRegReference(vregC);
- ArtMethod* method = FindMethodFromCode(method_idx, receiver, shadow_frame.GetMethod(), self,
- do_access_check, type);
- if (UNLIKELY(method == NULL)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- } else if (UNLIKELY(method->IsAbstract())) {
- ThrowAbstractMethodError(method);
- result->SetJ(0);
- return false;
- }
-
- MethodHelper mh(method);
- const DexFile::CodeItem* code_item = mh.GetCodeItem();
- uint16_t num_regs;
- uint16_t num_ins;
- if (LIKELY(code_item != NULL)) {
- num_regs = code_item->registers_size_;
- num_ins = code_item->ins_size_;
- } else {
- DCHECK(method->IsNative() || method->IsProxyMethod());
- num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty());
- if (!method->IsStatic()) {
- num_regs++;
- num_ins++;
- }
- }
-
- void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
- ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory));
- size_t cur_reg = num_regs - num_ins;
- if (receiver != NULL) {
- new_shadow_frame->SetVRegReference(cur_reg, receiver);
- ++cur_reg;
- }
-
- const DexFile::TypeList* params;
- if (do_assignability_check) {
- params = mh.GetParameterTypeList();
- }
- size_t arg_offset = (receiver == NULL) ? 0 : 1;
- const char* shorty = mh.GetShorty();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetArgs(arg);
- }
- for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
- DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
- size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
- switch (shorty[shorty_pos + 1]) {
- case 'L': {
- Object* o = shadow_frame.GetVRegReference(arg_pos);
- if (do_assignability_check && o != NULL) {
- Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_);
- if (arg_type == NULL) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- if (!o->VerifierInstanceOf(arg_type)) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
- mh.GetName(), shorty_pos,
- ClassHelper(o->GetClass()).GetDescriptor(),
- ClassHelper(arg_type).GetDescriptor());
- return false;
- }
- }
- new_shadow_frame->SetVRegReference(cur_reg, o);
- break;
- }
- case 'J': case 'D': {
- uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
- static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
- new_shadow_frame->SetVRegLong(cur_reg, wide_value);
- cur_reg++;
- arg_offset++;
- break;
- }
- default:
- new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
- break;
- }
- }
-
- if (LIKELY(Runtime::Current()->IsStarted())) {
- (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
- } else {
- UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
- }
- return !self->IsExceptionPending();
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<bool is_range>
-static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result)
- NO_THREAD_SAFETY_ANALYSIS;
-
-template<bool is_range>
-static bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, JValue* result) {
- uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- Object* receiver = shadow_frame.GetVRegReference(vregC);
- if (UNLIKELY(receiver == NULL)) {
- // We lost the reference to the method index so we cannot get a more
- // precised exception message.
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- return false;
- }
- uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- // TODO: use ObjectArray<T>::GetWithoutChecks ?
- ArtMethod* method = receiver->GetClass()->GetVTable()->Get(vtable_idx);
- if (UNLIKELY(method == NULL)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- } else if (UNLIKELY(method->IsAbstract())) {
- ThrowAbstractMethodError(method);
- result->SetJ(0);
- return false;
- }
-
- MethodHelper mh(method);
- const DexFile::CodeItem* code_item = mh.GetCodeItem();
- uint16_t num_regs;
- uint16_t num_ins;
- if (code_item != NULL) {
- num_regs = code_item->registers_size_;
- num_ins = code_item->ins_size_;
- } else {
- DCHECK(method->IsNative() || method->IsProxyMethod());
- num_regs = num_ins = ArtMethod::NumArgRegisters(mh.GetShorty());
- if (!method->IsStatic()) {
- num_regs++;
- num_ins++;
- }
- }
-
- void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
- ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame,
- method, 0, memory));
- size_t cur_reg = num_regs - num_ins;
- if (receiver != NULL) {
- new_shadow_frame->SetVRegReference(cur_reg, receiver);
- ++cur_reg;
- }
-
- size_t arg_offset = (receiver == NULL) ? 0 : 1;
- const char* shorty = mh.GetShorty();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetArgs(arg);
- }
- for (size_t shorty_pos = 0; cur_reg < num_regs; ++shorty_pos, cur_reg++, arg_offset++) {
- DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
- size_t arg_pos = is_range ? vregC + arg_offset : arg[arg_offset];
- switch (shorty[shorty_pos + 1]) {
- case 'L': {
- Object* o = shadow_frame.GetVRegReference(arg_pos);
- new_shadow_frame->SetVRegReference(cur_reg, o);
- break;
- }
- case 'J': case 'D': {
- uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(arg_pos + 1)) << 32) |
- static_cast<uint32_t>(shadow_frame.GetVReg(arg_pos));
- new_shadow_frame->SetVRegLong(cur_reg, wide_value);
- cur_reg++;
- arg_offset++;
- break;
- }
- default:
- new_shadow_frame->SetVReg(cur_reg, shadow_frame.GetVReg(arg_pos));
- break;
- }
- }
-
- if (LIKELY(Runtime::Current()->IsStarted())) {
- (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
- } else {
- UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, num_regs - num_ins);
- }
- return !self->IsExceptionPending();
-}
-
-// We use template functions to optimize compiler inlining process. Otherwise,
-// some parts of the code (like a switch statement) which depend on a constant
-// parameter would not be inlined while it should be. These constant parameters
-// are now part of the template arguments.
-// Note these template functions are static and inlined so they should not be
-// part of the final object file.
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst) {
- bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self,
- find_type, Primitive::FieldSize(field_type),
- do_access_check);
- if (UNLIKELY(f == NULL)) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- Object* obj;
- if (is_static) {
- obj = f->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true);
- return false;
- }
- }
- uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimBoolean:
- shadow_frame.SetVReg(vregA, f->GetBoolean(obj));
- break;
- case Primitive::kPrimByte:
- shadow_frame.SetVReg(vregA, f->GetByte(obj));
- break;
- case Primitive::kPrimChar:
- shadow_frame.SetVReg(vregA, f->GetChar(obj));
- break;
- case Primitive::kPrimShort:
- shadow_frame.SetVReg(vregA, f->GetShort(obj));
- break;
- case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, f->GetInt(obj));
- break;
- case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, f->GetLong(obj));
- break;
- case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, f->GetObject(obj));
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<Primitive::Type field_type>
-static bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<Primitive::Type field_type>
-static inline bool DoIGetQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst) {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- // We lost the reference to the field index so we cannot get a more
- // precised exception message.
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- return false;
- }
- MemberOffset field_offset(inst->VRegC_22c());
- const bool is_volatile = false; // iget-x-quick only on non volatile fields.
- const uint32_t vregA = inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
- break;
- case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
- break;
- case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
-static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
- const Instruction* inst) {
- bool do_assignability_check = do_access_check;
- bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* f = FindFieldFromCode(field_idx, shadow_frame.GetMethod(), self,
- find_type, Primitive::FieldSize(field_type),
- do_access_check);
- if (UNLIKELY(f == NULL)) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- Object* obj;
- if (is_static) {
- obj = f->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(),
- f, false);
- return false;
- }
- }
- uint32_t vregA = is_static ? inst->VRegA_21c() : inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimBoolean:
- f->SetBoolean(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimByte:
- f->SetByte(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimChar:
- f->SetChar(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimShort:
- f->SetShort(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimInt:
- f->SetInt(obj, shadow_frame.GetVReg(vregA));
- break;
- case Primitive::kPrimLong:
- f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
- break;
- case Primitive::kPrimNot: {
- Object* reg = shadow_frame.GetVRegReference(vregA);
- if (do_assignability_check && reg != NULL) {
- Class* field_class = FieldHelper(f).GetType();
- if (!reg->VerifierInstanceOf(field_class)) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Put '%s' that is not instance of field '%s' in '%s'",
- ClassHelper(reg->GetClass()).GetDescriptor(),
- ClassHelper(field_class).GetDescriptor(),
- ClassHelper(f->GetDeclaringClass()).GetDescriptor());
- return false;
- }
- }
- f->SetObj(obj, reg);
- break;
- }
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<Primitive::Type field_type>
-static bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template<Primitive::Type field_type>
-static inline bool DoIPutQuick(Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst) {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- if (UNLIKELY(obj == NULL)) {
- // We lost the reference to the field index so we cannot get a more
- // precised exception message.
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- return false;
- }
- MemberOffset field_offset(inst->VRegC_22c());
- const bool is_volatile = false; // iput-x-quick only on non volatile fields.
- const uint32_t vregA = inst->VRegA_22c();
- switch (field_type) {
- case Primitive::kPrimInt:
- obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
- break;
- case Primitive::kPrimLong:
- obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
- break;
- case Primitive::kPrimNot:
- obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
- break;
- default:
- LOG(FATAL) << "Unreachable: " << field_type;
- }
- return true;
-}
-
-static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- Class* java_lang_string_class = String::GetJavaLangString();
- if (UNLIKELY(!java_lang_string_class->IsInitialized())) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class,
- true, true))) {
- DCHECK(self->IsExceptionPending());
- return NULL;
- }
- }
- return mh.ResolveString(string_idx);
-}
-
-static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg,
- int32_t dividend, int32_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
- shadow_frame.SetVReg(result_reg, kMinInt);
- } else {
- shadow_frame.SetVReg(result_reg, dividend / divisor);
- }
- return true;
-}
-
-static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg,
- int32_t dividend, int32_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
- shadow_frame.SetVReg(result_reg, 0);
- } else {
- shadow_frame.SetVReg(result_reg, dividend % divisor);
- }
- return true;
-}
-
-static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg,
- int64_t dividend, int64_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
- shadow_frame.SetVRegLong(result_reg, kMinLong);
- } else {
- shadow_frame.SetVRegLong(result_reg, dividend / divisor);
- }
- return true;
-}
-
-static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg,
- int64_t dividend, int64_t divisor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- if (UNLIKELY(divisor == 0)) {
- ThrowArithmeticExceptionDivideByZero();
- return false;
- }
- if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
- shadow_frame.SetVRegLong(result_reg, 0);
- } else {
- shadow_frame.SetVRegLong(result_reg, dividend % divisor);
- }
- return true;
-}
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-// Returns true on success, otherwise throws an exception and returns false.
-template <bool is_range, bool do_access_check>
-static bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
- Thread* self, JValue* result)
- NO_THREAD_SAFETY_ANALYSIS ALWAYS_INLINE;
-
-template <bool is_range, bool do_access_check>
-static inline bool DoFilledNewArray(const Instruction* inst,
- const ShadowFrame& shadow_frame,
- Thread* self, JValue* result) {
- DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
- inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE);
- const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
- if (!is_range) {
- // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments.
- CHECK_LE(length, 5);
- }
- if (UNLIKELY(length < 0)) {
- ThrowNegativeArraySizeException(length);
- return false;
- }
- uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
- Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(arrayClass == NULL)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
- CHECK(arrayClass->IsArrayClass());
- Class* componentClass = arrayClass->GetComponentType();
- if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) {
- if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) {
- ThrowRuntimeException("Bad filled array request for type %s",
- PrettyDescriptor(componentClass).c_str());
- } else {
- self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
- "Ljava/lang/InternalError;",
- "Found type %s; filled-new-array not implemented for anything but \'int\'",
- PrettyDescriptor(componentClass).c_str());
- }
- return false;
- }
- Object* newArray = Array::Alloc(self, arrayClass, length);
- if (UNLIKELY(newArray == NULL)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
- if (is_range) {
- uint32_t vregC = inst->VRegC_3rc();
- const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
- for (int32_t i = 0; i < length; ++i) {
- if (is_primitive_int_component) {
- newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i));
- } else {
- newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i));
- }
- }
- } else {
- uint32_t arg[5];
- inst->GetArgs(arg);
- const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
- for (int32_t i = 0; i < length; ++i) {
- if (is_primitive_int_component) {
- newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i]));
- } else {
- newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i]));
- }
- }
- }
-
- result->SetL(newArray);
- return true;
-}
-
-static inline const Instruction* DoSparseSwitch(const Instruction* inst,
- const ShadowFrame& shadow_frame)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH);
- const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t());
- DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
- uint16_t size = switch_data[1];
- DCHECK_GT(size, 0);
- const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
- DCHECK(IsAligned<4>(keys));
- const int32_t* entries = keys + size;
- DCHECK(IsAligned<4>(entries));
- int lo = 0;
- int hi = size - 1;
- while (lo <= hi) {
- int mid = (lo + hi) / 2;
- int32_t foundVal = keys[mid];
- if (test_val < foundVal) {
- hi = mid - 1;
- } else if (test_val > foundVal) {
- lo = mid + 1;
- } else {
- return inst->RelativeAt(entries[mid]);
- }
- }
- return inst->Next_3xx();
-}
-
-static inline const Instruction* FindNextInstructionFollowingException(Thread* self,
- ShadowFrame& shadow_frame,
- uint32_t dex_pc,
- const uint16_t* insns,
- SirtRef<Object>& this_object_ref,
- instrumentation::Instrumentation* instrumentation)
- ALWAYS_INLINE;
-
-static inline const Instruction* FindNextInstructionFollowingException(Thread* self,
- ShadowFrame& shadow_frame,
- uint32_t dex_pc,
- const uint16_t* insns,
- SirtRef<Object>& this_object_ref,
- instrumentation::Instrumentation* instrumentation)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- self->VerifyStack();
- ThrowLocation throw_location;
- mirror::Throwable* exception = self->GetException(&throw_location);
- bool clear_exception;
- uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc,
- &clear_exception);
- if (found_dex_pc == DexFile::kDexNoIndex) {
- instrumentation->MethodUnwindEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), dex_pc);
- return NULL;
- } else {
- instrumentation->ExceptionCaughtEvent(self, throw_location,
- shadow_frame.GetMethod(),
- found_dex_pc, exception);
- if (clear_exception) {
- self->ClearException();
- }
- return Instruction::At(insns + found_dex_pc);
- }
-}
-
-#define HANDLE_PENDING_EXCEPTION() \
- CHECK(self->IsExceptionPending()); \
- inst = FindNextInstructionFollowingException(self, shadow_frame, inst->GetDexPc(insns), insns, \
- this_object_ref, instrumentation); \
- if (inst == NULL) { \
- return JValue(); /* Handled in caller. */ \
- }
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION(is_exception_pending, next_function) \
- if (UNLIKELY(is_exception_pending)) { \
- HANDLE_PENDING_EXCEPTION(); \
- } else { \
- inst = inst->next_function(); \
- }
-
-static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
- __attribute__((cold, noreturn, noinline));
-
-static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile());
- exit(0); // Unreachable, keep GCC happy.
-}
-
-// Code to run before each dex instruction.
-#define PREAMBLE()
-
-// TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) which is failing due to template
-// specialization.
-template<bool do_access_check>
-static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register)
- NO_THREAD_SAFETY_ANALYSIS __attribute__((hot));
-
-template<bool do_access_check>
-static JValue ExecuteImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
- ShadowFrame& shadow_frame, JValue result_register) {
- bool do_assignability_check = do_access_check;
- if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
- LOG(FATAL) << "Invalid shadow frame for interpreter use";
- return JValue();
- }
- self->VerifyStack();
- instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
+enum InterpreterImplKind {
+ kSwitchImpl, // switch-based interpreter implementation.
+ kComputedGotoImplKind // computed-goto-based interpreter implementation.
+};
- // As the 'this' object won't change during the execution of current code, we
- // want to cache it in local variables. Nevertheless, in order to let the
- // garbage collector access it, we store it into sirt references.
- SirtRef<Object> this_object_ref(self, shadow_frame.GetThisObject(code_item->ins_size_));
-
- uint32_t dex_pc = shadow_frame.GetDexPC();
- if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
- if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
- instrumentation->MethodEnterEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), 0);
- }
- }
- const uint16_t* const insns = code_item->insns_;
- const Instruction* inst = Instruction::At(insns + dex_pc);
- while (true) {
- dex_pc = inst->GetDexPc(insns);
- shadow_frame.SetDexPC(dex_pc);
- if (UNLIKELY(self->TestAllFlags())) {
- CheckSuspend(self);
- }
- if (UNLIKELY(instrumentation->HasDexPcListeners())) {
- instrumentation->DexPcMovedEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), dex_pc);
- }
- const bool kTracing = false;
- if (kTracing) {
-#define TRACE_LOG std::cerr
- TRACE_LOG << PrettyMethod(shadow_frame.GetMethod())
- << StringPrintf("\n0x%x: ", dex_pc)
- << inst->DumpString(&mh.GetDexFile()) << "\n";
- for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
- uint32_t raw_value = shadow_frame.GetVReg(i);
- Object* ref_value = shadow_frame.GetVRegReference(i);
- TRACE_LOG << StringPrintf(" vreg%d=0x%08X", i, raw_value);
- if (ref_value != NULL) {
- if (ref_value->GetClass()->IsStringClass() &&
- ref_value->AsString()->GetCharArray() != NULL) {
- TRACE_LOG << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";
- } else {
- TRACE_LOG << "/" << PrettyTypeOf(ref_value);
- }
- }
- }
- TRACE_LOG << "\n";
-#undef TRACE_LOG
- }
- switch (inst->Opcode()) {
- case Instruction::NOP:
- PREAMBLE();
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_FROM16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22x(),
- shadow_frame.GetVReg(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_32x(),
- shadow_frame.GetVReg(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(),
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_WIDE_FROM16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_22x(),
- shadow_frame.GetVRegLong(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_WIDE_16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_32x(),
- shadow_frame.GetVRegLong(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_OBJECT:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_12x(),
- shadow_frame.GetVRegReference(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_OBJECT_FROM16:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_22x(),
- shadow_frame.GetVRegReference(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_OBJECT_16:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_32x(),
- shadow_frame.GetVRegReference(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_RESULT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_11x(), result_register.GetI());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_RESULT_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_11x(), result_register.GetJ());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_RESULT_OBJECT:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_11x(), result_register.GetL());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_EXCEPTION: {
- PREAMBLE();
- Throwable* exception = self->GetException(NULL);
- self->ClearException();
- shadow_frame.SetVRegReference(inst->VRegA_11x(), exception);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::RETURN_VOID: {
- PREAMBLE();
- JValue result;
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN_VOID_BARRIER: {
- PREAMBLE();
- ANDROID_MEMBAR_STORE();
- JValue result;
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN: {
- PREAMBLE();
- JValue result;
- result.SetJ(0);
- result.SetI(shadow_frame.GetVReg(inst->VRegA_11x()));
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN_WIDE: {
- PREAMBLE();
- JValue result;
- result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x()));
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::RETURN_OBJECT: {
- PREAMBLE();
- JValue result;
- Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x());
- result.SetJ(0);
- result.SetL(obj_result);
- if (do_assignability_check && obj_result != NULL) {
- Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
- if (return_type == NULL) {
- // Return the pending exception.
- HANDLE_PENDING_EXCEPTION();
- }
- if (!obj_result->VerifierInstanceOf(return_type)) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Returning '%s' that is not instance of return type '%s'",
- ClassHelper(obj_result->GetClass()).GetDescriptor(),
- ClassHelper(return_type).GetDescriptor());
- HANDLE_PENDING_EXCEPTION();
- }
- }
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
- instrumentation->MethodExitEvent(self, this_object_ref.get(),
- shadow_frame.GetMethod(), inst->GetDexPc(insns),
- result);
- }
- return result;
- }
- case Instruction::CONST_4: {
- PREAMBLE();
- uint4_t dst = inst->VRegA_11n();
- int4_t val = inst->VRegB_11n();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::CONST_16: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_21s();
- int16_t val = inst->VRegB_21s();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CONST: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_31i();
- int32_t val = inst->VRegB_31i();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_3xx();
- break;
- }
- case Instruction::CONST_HIGH16: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_21h();
- int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, NULL);
- }
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CONST_WIDE_16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_21s(), inst->VRegB_21s());
- inst = inst->Next_2xx();
- break;
- case Instruction::CONST_WIDE_32:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_31i(), inst->VRegB_31i());
- inst = inst->Next_3xx();
- break;
- case Instruction::CONST_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_51l(), inst->VRegB_51l());
- inst = inst->Next_51l();
- break;
- case Instruction::CONST_WIDE_HIGH16:
- shadow_frame.SetVRegLong(inst->VRegA_21h(),
- static_cast<uint64_t>(inst->VRegB_21h()) << 48);
- inst = inst->Next_2xx();
- break;
- case Instruction::CONST_STRING: {
- PREAMBLE();
- String* s = ResolveString(self, mh, inst->VRegB_21c());
- if (UNLIKELY(s == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(), s);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::CONST_STRING_JUMBO: {
- PREAMBLE();
- String* s = ResolveString(self, mh, inst->VRegB_31c());
- if (UNLIKELY(s == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_31c(), s);
- inst = inst->Next_3xx();
- }
- break;
- }
- case Instruction::CONST_CLASS: {
- PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(c == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(), c);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::MONITOR_ENTER: {
- PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- } else {
- DoMonitorEnter(self, obj);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- }
- break;
- }
- case Instruction::MONITOR_EXIT: {
- PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- } else {
- DoMonitorExit(self, obj);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- }
- break;
- }
- case Instruction::CHECK_CAST: {
- PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(c == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c());
- if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
- ThrowClassCastException(c, obj->GetClass());
- HANDLE_PENDING_EXCEPTION();
- } else {
- inst = inst->Next_2xx();
- }
- }
- break;
- }
- case Instruction::INSTANCE_OF: {
- PREAMBLE();
- Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
- self, false, do_access_check);
- if (UNLIKELY(c == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c());
- shadow_frame.SetVReg(inst->VRegA_22c(), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::ARRAY_LENGTH: {
- PREAMBLE();
- Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x());
- if (UNLIKELY(array == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVReg(inst->VRegA_12x(), array->AsArray()->GetLength());
- inst = inst->Next_1xx();
- }
- break;
- }
- case Instruction::NEW_INSTANCE: {
- PREAMBLE();
- Object* obj = AllocObjectFromCode(inst->VRegB_21c(), shadow_frame.GetMethod(),
- self, do_access_check);
- if (UNLIKELY(obj == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(), obj);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::NEW_ARRAY: {
- PREAMBLE();
- int32_t length = shadow_frame.GetVReg(inst->VRegB_22c());
- Object* obj = AllocArrayFromCode(inst->VRegC_22c(), shadow_frame.GetMethod(),
- length, self, do_access_check);
- if (UNLIKELY(obj == NULL)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_22c(), obj);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::FILLED_NEW_ARRAY: {
- PREAMBLE();
- bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
- self, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::FILLED_NEW_ARRAY_RANGE: {
- PREAMBLE();
- bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
- self, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::FILL_ARRAY_DATA: {
- PREAMBLE();
- Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t());
- if (UNLIKELY(obj == NULL)) {
- ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- Array* array = obj->AsArray();
- DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
- const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- const Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
- if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
- self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
- "Ljava/lang/ArrayIndexOutOfBoundsException;",
- "failed FILL_ARRAY_DATA; length=%d, index=%d",
- array->GetLength(), payload->element_count);
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint32_t size_in_bytes = payload->element_count * payload->element_width;
- memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes);
- inst = inst->Next_3xx();
- break;
- }
- case Instruction::THROW: {
- PREAMBLE();
- Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x());
- if (UNLIKELY(exception == NULL)) {
- ThrowNullPointerException(NULL, "throw with null exception");
- } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
- // This should never happen.
- self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
- "Ljava/lang/VirtualMachineError;",
- "Throwing '%s' that is not instance of Throwable",
- ClassHelper(exception->GetClass()).GetDescriptor());
- } else {
- self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
- }
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- case Instruction::GOTO: {
- PREAMBLE();
- inst = inst->RelativeAt(inst->VRegA_10t());
- break;
- }
- case Instruction::GOTO_16: {
- PREAMBLE();
- inst = inst->RelativeAt(inst->VRegA_20t());
- break;
- }
- case Instruction::GOTO_32: {
- PREAMBLE();
- inst = inst->RelativeAt(inst->VRegA_30t());
- break;
- }
- case Instruction::PACKED_SWITCH: {
- PREAMBLE();
- const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t());
- DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
- uint16_t size = switch_data[1];
- DCHECK_GT(size, 0);
- const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
- DCHECK(IsAligned<4>(keys));
- int32_t first_key = keys[0];
- const int32_t* targets = reinterpret_cast<const int32_t*>(&switch_data[4]);
- DCHECK(IsAligned<4>(targets));
- int32_t index = test_val - first_key;
- if (index >= 0 && index < size) {
- inst = inst->RelativeAt(targets[index]);
- } else {
- inst = inst->Next_3xx();
- }
- break;
- }
- case Instruction::SPARSE_SWITCH: {
- PREAMBLE();
- inst = DoSparseSwitch(inst, shadow_frame);
- break;
- }
- case Instruction::CMPL_FLOAT: {
- PREAMBLE();
- float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
- float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMPG_FLOAT: {
- PREAMBLE();
- float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
- float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
- int32_t result;
- if (val1 < val2) {
- result = -1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = 1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMPL_DOUBLE: {
- PREAMBLE();
- double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
- double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
-
- case Instruction::CMPG_DOUBLE: {
- PREAMBLE();
- double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
- double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
- int32_t result;
- if (val1 < val2) {
- result = -1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = 1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMP_LONG: {
- PREAMBLE();
- int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
- int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::IF_EQ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) == shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_NE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) != shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LT: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) < shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) >= shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GT: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) > shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t()) <= shadow_frame.GetVReg(inst->VRegB_22t())) {
- inst = inst->RelativeAt(inst->VRegC_22t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_EQZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) == 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_NEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) != 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LTZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) < 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) >= 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GTZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) > 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t()) <= 0) {
- inst = inst->RelativeAt(inst->VRegB_21t());
- } else {
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::AGET_BOOLEAN: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- BooleanArray* array = a->AsBooleanArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_BYTE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ByteArray* array = a->AsByteArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_CHAR: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- CharArray* array = a->AsCharArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_SHORT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ShortArray* array = a->AsShortArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- IntArray* array = a->AsIntArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVReg(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_WIDE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- LongArray* array = a->AsLongArray();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVRegLong(inst->VRegA_23x(), array->GetData()[index]);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_OBJECT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
- if (LIKELY(array->IsValidIndex(index))) {
- shadow_frame.SetVRegReference(inst->VRegA_23x(), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_BOOLEAN: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- BooleanArray* array = a->AsBooleanArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_BYTE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int8_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ByteArray* array = a->AsByteArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_CHAR: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- CharArray* array = a->AsCharArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_SHORT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int16_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ShortArray* array = a->AsShortArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t val = shadow_frame.GetVReg(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- IntArray* array = a->AsIntArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_WIDE: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x());
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- LongArray* array = a->AsLongArray();
- if (LIKELY(array->IsValidIndex(index))) {
- array->GetData()[index] = val;
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_OBJECT: {
- PREAMBLE();
- Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == NULL)) {
- ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x());
- ObjectArray<Object>* array = a->AsObjectArray<Object>();
- if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) {
- array->SetWithoutChecks(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::IGET_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_BYTE: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_CHAR: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_SHORT: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_WIDE: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_OBJECT: {
- PREAMBLE();
- bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_WIDE_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_OBJECT_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_BYTE: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_CHAR: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_SHORT: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_WIDE: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_OBJECT: {
- PREAMBLE();
- bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BYTE: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_CHAR: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_SHORT: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_WIDE: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_OBJECT: {
- PREAMBLE();
- bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimInt>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_WIDE_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimLong>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_OBJECT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimNot>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_BYTE: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_CHAR: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_SHORT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_WIDE: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_OBJECT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_SUPER: {
- PREAMBLE();
- bool success = DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_SUPER_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_DIRECT: {
- PREAMBLE();
- bool success = DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_DIRECT_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_INTERFACE: {
- PREAMBLE();
- bool success = DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_INTERFACE_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_STATIC: {
- PREAMBLE();
- bool success = DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_STATIC_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_QUICK: {
- PREAMBLE();
- bool success = DoInvokeVirtualQuick<false>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
- PREAMBLE();
- bool success = DoInvokeVirtualQuick<true>(self, shadow_frame, inst, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::NEG_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(), -shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NOT_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(), ~shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(), -shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NOT_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(), ~shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), -shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), -shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::FLOAT_TO_INT: {
- PREAMBLE();
- float val = shadow_frame.GetVRegFloat(inst->VRegB_12x());
- int32_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<float>(kMaxInt)) {
- result = kMaxInt;
- } else if (val < static_cast<float>(kMinInt)) {
- result = kMinInt;
- } else {
- result = val;
- }
- shadow_frame.SetVReg(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::FLOAT_TO_LONG: {
- PREAMBLE();
- float val = shadow_frame.GetVRegFloat(inst->VRegB_12x());
- int64_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<float>(kMaxLong)) {
- result = kMaxLong;
- } else if (val < static_cast<float>(kMinLong)) {
- result = kMinLong;
- } else {
- result = val;
- }
- shadow_frame.SetVRegLong(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::FLOAT_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(), shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::DOUBLE_TO_INT: {
- PREAMBLE();
- double val = shadow_frame.GetVRegDouble(inst->VRegB_12x());
- int32_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<double>(kMaxInt)) {
- result = kMaxInt;
- } else if (val < static_cast<double>(kMinInt)) {
- result = kMinInt;
- } else {
- result = val;
- }
- shadow_frame.SetVReg(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DOUBLE_TO_LONG: {
- PREAMBLE();
- double val = shadow_frame.GetVRegDouble(inst->VRegB_12x());
- int64_t result;
- if (val != val) {
- result = 0;
- } else if (val > static_cast<double>(kMaxLong)) {
- result = kMaxLong;
- } else if (val < static_cast<double>(kMinLong)) {
- result = kMinLong;
- } else {
- result = val;
- }
- shadow_frame.SetVRegLong(inst->VRegA_12x(), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DOUBLE_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(), shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_BYTE:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_CHAR:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_SHORT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(),
- static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- case Instruction::ADD_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) +
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) -
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) *
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SHL_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) <<
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::AND_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) &
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) |
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(),
- shadow_frame.GetVReg(inst->VRegB_23x()) ^
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) +
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) -
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) *
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_LONG:
- PREAMBLE();
- DoLongDivide(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
- break;
- case Instruction::REM_LONG:
- PREAMBLE();
- DoLongRemainder(shadow_frame, inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
- break;
- case Instruction::AND_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) &
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) |
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHL_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(),
- static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::REM_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(),
- fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
- shadow_frame.GetVRegFloat(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::REM_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(),
- fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
- shadow_frame.GetVRegDouble(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) +
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) -
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) *
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
- break;
- }
- case Instruction::REM_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
- break;
- }
- case Instruction::SHL_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) <<
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::USHR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::AND_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) &
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::OR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) |
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::XOR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) ^
- shadow_frame.GetVReg(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) +
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) -
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) *
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- break;
- }
- case Instruction::REM_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- break;
- }
- case Instruction::AND_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) &
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::OR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) |
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::XOR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) ^
- shadow_frame.GetVRegLong(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHL_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) <<
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::USHR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegLong(vregA,
- static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
- (shadow_frame.GetVReg(inst->VRegB_12x()) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) +
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) -
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) *
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) /
- shadow_frame.GetVRegFloat(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::REM_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegFloat(vregA,
- fmodf(shadow_frame.GetVRegFloat(vregA),
- shadow_frame.GetVRegFloat(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) +
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) -
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) *
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) /
- shadow_frame.GetVRegDouble(inst->VRegB_12x()));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::REM_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x();
- shadow_frame.SetVRegDouble(vregA,
- fmod(shadow_frame.GetVRegDouble(vregA),
- shadow_frame.GetVRegDouble(inst->VRegB_12x())));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) +
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::RSUB_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- inst->VRegC_22s() -
- shadow_frame.GetVReg(inst->VRegB_22s()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) *
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT_LIT16: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT_LIT16: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()), inst->VRegC_22s());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::AND_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) &
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) |
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s()) ^
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) +
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::RSUB_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- inst->VRegC_22b() -
- shadow_frame.GetVReg(inst->VRegB_22b()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) *
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT_LIT8: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT_LIT8: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::AND_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) &
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) |
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) ^
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::SHL_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) <<
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- shadow_frame.GetVReg(inst->VRegB_22b()) >>
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(),
- static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
- case Instruction::UNUSED_79:
- case Instruction::UNUSED_7A:
- UnexpectedOpcode(inst, mh);
- }
- }
-} // NOLINT(readability/fn_size)
+static const InterpreterImplKind kInterpreterImplKind = kComputedGotoImplKind;
static JValue Execute(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register)
@@ -3114,12 +281,23 @@ static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::Code
shadow_frame.GetMethod()->GetDeclaringClass()->IsProxyClass());
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
- if (shadow_frame.GetMethod()->IsPreverified()) {
+
+ if (LIKELY(shadow_frame.GetMethod()->IsPreverified())) {
// Enter the "without access check" interpreter.
- return ExecuteImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ if (kInterpreterImplKind == kSwitchImpl) {
+ return ExecuteSwitchImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
+ return ExecuteGotoImpl<false>(self, mh, code_item, shadow_frame, result_register);
+ }
} else {
// Enter the "with access check" interpreter.
- return ExecuteImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ if (kInterpreterImplKind == kSwitchImpl) {
+ return ExecuteSwitchImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ } else {
+ DCHECK_EQ(kInterpreterImplKind, kComputedGotoImplKind);
+ return ExecuteGotoImpl<true>(self, mh, code_item, shadow_frame, result_register);
+ }
}
}
@@ -3131,6 +309,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive
return;
}
+ const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
MethodHelper mh(method);
const DexFile::CodeItem* code_item = mh.GetCodeItem();
uint16_t num_regs;
@@ -3139,6 +318,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive
num_regs = code_item->registers_size_;
num_ins = code_item->ins_size_;
} else if (method->IsAbstract()) {
+ self->EndAssertNoThreadSuspension(old_cause);
ThrowAbstractMethodError(method);
return;
} else {
@@ -3154,6 +334,8 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive
void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, last_shadow_frame, method, 0, memory));
self->PushShadowFrame(shadow_frame);
+ self->EndAssertNoThreadSuspension(old_cause);
+
size_t cur_reg = num_regs - num_ins;
if (!method->IsStatic()) {
CHECK(receiver != NULL);
@@ -3161,8 +343,7 @@ void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receive
++cur_reg;
} else if (UNLIKELY(!method->GetDeclaringClass()->IsInitializing())) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(),
- true, true))) {
+ if (UNLIKELY(!class_linker->EnsureInitialized(method->GetDeclaringClass(), true, true))) {
CHECK(self->IsExceptionPending());
self->PopShadowFrame();
return;
@@ -3243,31 +424,34 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh
return;
}
+ self->PushShadowFrame(shadow_frame);
ArtMethod* method = shadow_frame->GetMethod();
- if (method->IsStatic() && !method->GetDeclaringClass()->IsInitializing()) {
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(method->GetDeclaringClass(),
- true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return;
+ // Ensure static methods are initialized.
+ if (method->IsStatic()) {
+ Class* declaringClass = method->GetDeclaringClass();
+ if (UNLIKELY(!declaringClass->IsInitializing())) {
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass,
+ true, true))) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ self->PopShadowFrame();
+ return;
+ }
+ CHECK(declaringClass->IsInitializing());
}
- CHECK(method->GetDeclaringClass()->IsInitializing());
}
- self->PushShadowFrame(shadow_frame);
-
if (LIKELY(!method->IsNative())) {
result->SetJ(Execute(self, mh, code_item, *shadow_frame, JValue()).GetJ());
} else {
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
CHECK(!Runtime::Current()->IsStarted());
- Object* receiver = method->IsStatic() ? NULL : shadow_frame->GetVRegReference(0);
+ Object* receiver = method->IsStatic() ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(method->IsStatic() ? 0 : 1);
UnstartedRuntimeJni(self, method, receiver, args, result);
}
self->PopShadowFrame();
- return;
}
} // namespace interpreter
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
new file mode 100644
index 0000000000..19f55d2f97
--- /dev/null
+++ b/runtime/interpreter/interpreter_common.cc
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2012 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 "interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
+ JValue* result, size_t arg_offset)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame.
+static inline void AssignRegister(ShadowFrame& new_shadow_frame, const ShadowFrame& shadow_frame,
+ size_t dest_reg, size_t src_reg) {
+ // If both register locations contains the same value, the register probably holds a reference.
+ int32_t src_value = shadow_frame.GetVReg(src_reg);
+ mirror::Object* o = shadow_frame.GetVRegReference(src_reg);
+ if (src_value == reinterpret_cast<int32_t>(o)) {
+ new_shadow_frame.SetVRegReference(dest_reg, o);
+ } else {
+ new_shadow_frame.SetVReg(dest_reg, src_value);
+ }
+}
+
+template<bool is_range, bool do_assignability_check>
+bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data, JValue* result) {
+ // Compute method information.
+ MethodHelper mh(method);
+ const DexFile::CodeItem* code_item = mh.GetCodeItem();
+ const uint16_t num_ins = (is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
+ uint16_t num_regs;
+ if (LIKELY(code_item != NULL)) {
+ num_regs = code_item->registers_size_;
+ DCHECK_EQ(num_ins, code_item->ins_size_);
+ } else {
+ DCHECK(method->IsNative() || method->IsProxyMethod());
+ num_regs = num_ins;
+ }
+
+ // Allocate shadow frame on the stack.
+ const char* old_cause = self->StartAssertNoThreadSuspension("DoCall");
+ void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
+ ShadowFrame* new_shadow_frame(ShadowFrame::Create(num_regs, &shadow_frame, method, 0, memory));
+
+ // Initialize new shadow frame.
+ const size_t first_dest_reg = num_regs - num_ins;
+ if (do_assignability_check) {
+ // Slow path: we need to do runtime check on reference assignment. We need to load the shorty
+ // to get the exact type of each reference argument.
+ const DexFile::TypeList* params = mh.GetParameterTypeList();
+ const char* shorty = mh.GetShorty();
+
+ // Handle receiver apart since it's not part of the shorty.
+ size_t dest_reg = first_dest_reg;
+ size_t arg_offset = 0;
+ if (receiver != NULL) {
+ DCHECK(!method->IsStatic());
+ new_shadow_frame->SetVRegReference(dest_reg, receiver);
+ ++dest_reg;
+ ++arg_offset;
+ } else {
+ DCHECK(method->IsStatic());
+ }
+ // TODO: find a cleaner way to separate non-range and range information without duplicating code.
+ uint32_t arg[5]; // only used in invoke-XXX.
+ uint32_t vregC; // only used in invoke-XXX-range.
+ if (is_range) {
+ vregC = inst->VRegC_3rc();
+ } else {
+ inst->GetArgs(arg, inst_data);
+ }
+ for (size_t shorty_pos = 0; dest_reg < num_regs; ++shorty_pos, ++dest_reg, ++arg_offset) {
+ DCHECK_LT(shorty_pos + 1, mh.GetShortyLength());
+ const size_t src_reg = (is_range) ? vregC + arg_offset : arg[arg_offset];
+ switch (shorty[shorty_pos + 1]) {
+ case 'L': {
+ Object* o = shadow_frame.GetVRegReference(src_reg);
+ if (do_assignability_check && o != NULL) {
+ Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_);
+ if (arg_type == NULL) {
+ CHECK(self->IsExceptionPending());
+ self->EndAssertNoThreadSuspension(old_cause);
+ return false;
+ }
+ if (!o->VerifierInstanceOf(arg_type)) {
+ self->EndAssertNoThreadSuspension(old_cause);
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
+ mh.GetName(), shorty_pos,
+ ClassHelper(o->GetClass()).GetDescriptor(),
+ ClassHelper(arg_type).GetDescriptor());
+ return false;
+ }
+ }
+ new_shadow_frame->SetVRegReference(dest_reg, o);
+ break;
+ }
+ case 'J': case 'D': {
+ uint64_t wide_value = (static_cast<uint64_t>(shadow_frame.GetVReg(src_reg + 1)) << 32) |
+ static_cast<uint32_t>(shadow_frame.GetVReg(src_reg));
+ new_shadow_frame->SetVRegLong(dest_reg, wide_value);
+ ++dest_reg;
+ ++arg_offset;
+ break;
+ }
+ default:
+ new_shadow_frame->SetVReg(dest_reg, shadow_frame.GetVReg(src_reg));
+ break;
+ }
+ }
+ } else {
+ // Fast path: no extra checks.
+ if (is_range) {
+ const uint16_t first_src_reg = inst->VRegC_3rc();
+ for (size_t src_reg = first_src_reg, dest_reg = first_dest_reg; dest_reg < num_regs;
+ ++dest_reg, ++src_reg) {
+ AssignRegister(*new_shadow_frame, shadow_frame, dest_reg, src_reg);
+ }
+ } else {
+ DCHECK_LE(num_ins, 5U);
+ uint16_t regList = inst->Fetch16(2);
+ uint16_t count = num_ins;
+ if (count == 5) {
+ AssignRegister(*new_shadow_frame, shadow_frame, first_dest_reg + 4U, (inst_data >> 8) & 0x0f);
+ --count;
+ }
+ for (size_t arg_index = 0; arg_index < count; ++arg_index, regList >>= 4) {
+ AssignRegister(*new_shadow_frame, shadow_frame, first_dest_reg + arg_index, regList & 0x0f);
+ }
+ }
+ }
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ // Do the call now.
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ (method->GetEntryPointFromInterpreter())(self, mh, code_item, new_shadow_frame, result);
+ } else {
+ UnstartedRuntimeInvoke(self, mh, code_item, new_shadow_frame, result, first_dest_reg);
+ }
+ return !self->IsExceptionPending();
+}
+
+template <bool is_range, bool do_access_check>
+bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
+ Thread* self, JValue* result) {
+ DCHECK(inst->Opcode() == Instruction::FILLED_NEW_ARRAY ||
+ inst->Opcode() == Instruction::FILLED_NEW_ARRAY_RANGE);
+ const int32_t length = is_range ? inst->VRegA_3rc() : inst->VRegA_35c();
+ if (!is_range) {
+ // Checks FILLED_NEW_ARRAY's length does not exceed 5 arguments.
+ CHECK_LE(length, 5);
+ }
+ if (UNLIKELY(length < 0)) {
+ ThrowNegativeArraySizeException(length);
+ return false;
+ }
+ uint16_t type_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ Class* arrayClass = ResolveVerifyAndClinit(type_idx, shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(arrayClass == NULL)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ CHECK(arrayClass->IsArrayClass());
+ Class* componentClass = arrayClass->GetComponentType();
+ if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) {
+ if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) {
+ ThrowRuntimeException("Bad filled array request for type %s",
+ PrettyDescriptor(componentClass).c_str());
+ } else {
+ self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+ "Ljava/lang/InternalError;",
+ "Found type %s; filled-new-array not implemented for anything but \'int\'",
+ PrettyDescriptor(componentClass).c_str());
+ }
+ return false;
+ }
+ Object* newArray = Array::Alloc(self, arrayClass, length);
+ if (UNLIKELY(newArray == NULL)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ if (is_range) {
+ uint32_t vregC = inst->VRegC_3rc();
+ const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
+ for (int32_t i = 0; i < length; ++i) {
+ if (is_primitive_int_component) {
+ newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(vregC + i));
+ } else {
+ newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(vregC + i));
+ }
+ }
+ } else {
+ uint32_t arg[5];
+ inst->GetArgs(arg);
+ const bool is_primitive_int_component = componentClass->IsPrimitiveInt();
+ for (int32_t i = 0; i < length; ++i) {
+ if (is_primitive_int_component) {
+ newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(arg[i]));
+ } else {
+ newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(arg[i]));
+ }
+ }
+ }
+
+ result->SetL(newArray);
+ return true;
+}
+
+static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item, ShadowFrame* shadow_frame,
+ JValue* result, size_t arg_offset) {
+ // In a runtime that's not started we intercept certain methods to avoid complicated dependency
+ // problems in core libraries.
+ std::string name(PrettyMethod(shadow_frame->GetMethod()));
+ if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") {
+ std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str()));
+ ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader();
+ Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(),
+ class_loader);
+ CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: "
+ << PrettyDescriptor(descriptor);
+ result->SetL(found);
+ } else if (name == "java.lang.Object java.lang.Class.newInstance()") {
+ Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+ ArtMethod* c = klass->FindDeclaredDirectMethod("<init>", "()V");
+ CHECK(c != NULL);
+ SirtRef<Object> obj(self, klass->AllocObject(self));
+ CHECK(obj.get() != NULL);
+ EnterInterpreterFromInvoke(self, c, obj.get(), NULL, NULL);
+ result->SetL(obj.get());
+ } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
+ // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
+ // going the reflective Dex way.
+ Class* klass = shadow_frame->GetVRegReference(arg_offset)->AsClass();
+ String* name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
+ ArtField* found = NULL;
+ FieldHelper fh;
+ ObjectArray<ArtField>* fields = klass->GetIFields();
+ for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
+ ArtField* f = fields->Get(i);
+ fh.ChangeField(f);
+ if (name->Equals(fh.GetName())) {
+ found = f;
+ }
+ }
+ if (found == NULL) {
+ fields = klass->GetSFields();
+ for (int32_t i = 0; i < fields->GetLength() && found == NULL; ++i) {
+ ArtField* f = fields->Get(i);
+ fh.ChangeField(f);
+ if (name->Equals(fh.GetName())) {
+ found = f;
+ }
+ }
+ }
+ CHECK(found != NULL)
+ << "Failed to find field in Class.getDeclaredField in un-started runtime. name="
+ << name->ToModifiedUtf8() << " class=" << PrettyDescriptor(klass);
+ // TODO: getDeclaredField calls GetType once the field is found to ensure a
+ // NoClassDefFoundError is thrown if the field's type cannot be resolved.
+ Class* jlr_Field = self->DecodeJObject(WellKnownClasses::java_lang_reflect_Field)->AsClass();
+ SirtRef<Object> field(self, jlr_Field->AllocObject(self));
+ CHECK(field.get() != NULL);
+ ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("<init>", "(Ljava/lang/reflect/ArtField;)V");
+ uint32_t args[1];
+ args[0] = reinterpret_cast<uint32_t>(found);
+ EnterInterpreterFromInvoke(self, c, field.get(), args, NULL);
+ result->SetL(field.get());
+ } else if (name == "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)" ||
+ name == "void java.lang.System.arraycopy(char[], int, char[], int, int)") {
+ // Special case array copying without initializing System.
+ Class* ctype = shadow_frame->GetVRegReference(arg_offset)->GetClass()->GetComponentType();
+ jint srcPos = shadow_frame->GetVReg(arg_offset + 1);
+ jint dstPos = shadow_frame->GetVReg(arg_offset + 3);
+ jint length = shadow_frame->GetVReg(arg_offset + 4);
+ if (!ctype->IsPrimitive()) {
+ ObjectArray<Object>* src = shadow_frame->GetVRegReference(arg_offset)->AsObjectArray<Object>();
+ ObjectArray<Object>* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsObjectArray<Object>();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else if (ctype->IsPrimitiveChar()) {
+ CharArray* src = shadow_frame->GetVRegReference(arg_offset)->AsCharArray();
+ CharArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsCharArray();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else if (ctype->IsPrimitiveInt()) {
+ IntArray* src = shadow_frame->GetVRegReference(arg_offset)->AsIntArray();
+ IntArray* dst = shadow_frame->GetVRegReference(arg_offset + 2)->AsIntArray();
+ for (jint i = 0; i < length; ++i) {
+ dst->Set(dstPos + i, src->Get(srcPos + i));
+ }
+ } else {
+ UNIMPLEMENTED(FATAL) << "System.arraycopy of unexpected type: " << PrettyDescriptor(ctype);
+ }
+ } else {
+ // Not special, continue with regular interpreter execution.
+ artInterpreterToInterpreterBridge(self, mh, code_item, shadow_frame, result);
+ }
+}
+
+// Explicit DoCall template function declarations.
+#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Object* receiver, \
+ Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false);
+EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
+#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
+
+// Explicit DoFilledNewArray template function declarations.
+#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) \
+ bool DoFilledNewArray<_is_range_, _check>(const Instruction* inst, \
+ const ShadowFrame& shadow_frame, \
+ Thread* self, JValue* result)
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(false, true);
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, false);
+EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(true, true);
+#undef EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
new file mode 100644
index 0000000000..0bc834ccb1
--- /dev/null
+++ b/runtime/interpreter/interpreter_common.h
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2012 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_INTERPRETER_INTERPRETER_COMMON_H_
+#define ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_
+
+#include "interpreter.h"
+
+#include <math.h>
+
+#include "base/logging.h"
+#include "class_linker-inl.h"
+#include "common_throws.h"
+#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
+#include "dex_instruction.h"
+#include "entrypoints/entrypoint_utils.h"
+#include "gc/accounting/card_table-inl.h"
+#include "invoke_arg_array_builder.h"
+#include "nth_caller_visitor.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class.h"
+#include "mirror/class-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "object_utils.h"
+#include "ScopedLocalRef.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+#include "well_known_classes.h"
+
+using ::art::mirror::ArtField;
+using ::art::mirror::ArtMethod;
+using ::art::mirror::Array;
+using ::art::mirror::BooleanArray;
+using ::art::mirror::ByteArray;
+using ::art::mirror::CharArray;
+using ::art::mirror::Class;
+using ::art::mirror::ClassLoader;
+using ::art::mirror::IntArray;
+using ::art::mirror::LongArray;
+using ::art::mirror::Object;
+using ::art::mirror::ObjectArray;
+using ::art::mirror::ShortArray;
+using ::art::mirror::String;
+using ::art::mirror::Throwable;
+
+namespace art {
+namespace interpreter {
+
+// External references to both interpreter implementations.
+
+template<bool do_access_check>
+extern JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+template<bool do_access_check>
+extern JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+static inline void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+ ref->MonitorEnter(self);
+}
+
+static inline void DoMonitorExit(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
+ ref->MonitorExit(self);
+}
+
+// Invokes the given method. This is part of the invocation support and is used by DoInvoke and
+// DoInvokeVirtualQuick functions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<bool is_range, bool do_assignability_check>
+bool DoCall(ArtMethod* method, Object* receiver, Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data, JValue* result);
+
+// Handles invoke-XXX/range instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<InvokeType type, bool is_range, bool do_access_check>
+static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
+ uint16_t inst_data, JValue* result) {
+ const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ Object* receiver = (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
+ ArtMethod* const method = FindMethodFromCode<type, do_access_check>(method_idx, receiver,
+ shadow_frame.GetMethod(),
+ self);
+ if (type != kStatic) {
+ // Reload the vreg since the GC may have moved the object.
+ receiver = shadow_frame.GetVRegReference(vregC);
+ }
+ if (UNLIKELY(method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ } else if (UNLIKELY(method->IsAbstract())) {
+ ThrowAbstractMethodError(method);
+ result->SetJ(0);
+ return false;
+ } else {
+ return DoCall<is_range, do_access_check>(method, receiver, self, shadow_frame, inst,
+ inst_data, result);
+ }
+}
+
+// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<bool is_range>
+static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data,
+ JValue* result) {
+ const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ Object* const receiver = shadow_frame.GetVRegReference(vregC);
+ if (UNLIKELY(receiver == nullptr)) {
+ // We lost the reference to the method index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return false;
+ }
+ const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+ ArtMethod* const method = receiver->GetClass()->GetVTable()->GetWithoutChecks(vtable_idx);
+ if (UNLIKELY(method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ } else if (UNLIKELY(method->IsAbstract())) {
+ ThrowAbstractMethodError(method);
+ result->SetJ(0);
+ return false;
+ } else {
+ // No need to check since we've been quickened.
+ return DoCall<is_range, false>(method, receiver, self, shadow_frame, inst, inst_data, result);
+ }
+}
+
+// Handles iget-XXX and sget-XXX instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+static inline bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data) {
+ const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
+ const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
+ ArtField* f = FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
+ Primitive::FieldSize(field_type));
+ if (UNLIKELY(f == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ Object* obj;
+ if (is_static) {
+ obj = f->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true);
+ return false;
+ }
+ }
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ shadow_frame.SetVReg(vregA, f->GetBoolean(obj));
+ break;
+ case Primitive::kPrimByte:
+ shadow_frame.SetVReg(vregA, f->GetByte(obj));
+ break;
+ case Primitive::kPrimChar:
+ shadow_frame.SetVReg(vregA, f->GetChar(obj));
+ break;
+ case Primitive::kPrimShort:
+ shadow_frame.SetVReg(vregA, f->GetShort(obj));
+ break;
+ case Primitive::kPrimInt:
+ shadow_frame.SetVReg(vregA, f->GetInt(obj));
+ break;
+ case Primitive::kPrimLong:
+ shadow_frame.SetVRegLong(vregA, f->GetLong(obj));
+ break;
+ case Primitive::kPrimNot:
+ shadow_frame.SetVRegReference(vregA, f->GetObject(obj));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles iget-quick, iget-wide-quick and iget-object-quick instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<Primitive::Type field_type>
+static inline bool DoIGetQuick(ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return false;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iget-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ shadow_frame.SetVReg(vregA, static_cast<int32_t>(obj->GetField32(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimLong:
+ shadow_frame.SetVRegLong(vregA, static_cast<int64_t>(obj->GetField64(field_offset, is_volatile)));
+ break;
+ case Primitive::kPrimNot:
+ shadow_frame.SetVRegReference(vregA, obj->GetFieldObject<mirror::Object*>(field_offset, is_volatile));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles iput-XXX and sput-XXX instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
+static inline bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data) {
+ bool do_assignability_check = do_access_check;
+ bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
+ uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
+ ArtField* f = FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
+ Primitive::FieldSize(field_type));
+ if (UNLIKELY(f == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ Object* obj;
+ if (is_static) {
+ obj = f->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(),
+ f, false);
+ return false;
+ }
+ }
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ f->SetBoolean(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimByte:
+ f->SetByte(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimChar:
+ f->SetChar(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimShort:
+ f->SetShort(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimInt:
+ f->SetInt(obj, shadow_frame.GetVReg(vregA));
+ break;
+ case Primitive::kPrimLong:
+ f->SetLong(obj, shadow_frame.GetVRegLong(vregA));
+ break;
+ case Primitive::kPrimNot: {
+ Object* reg = shadow_frame.GetVRegReference(vregA);
+ if (do_assignability_check && reg != nullptr) {
+ Class* field_class = FieldHelper(f).GetType();
+ if (!reg->VerifierInstanceOf(field_class)) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Put '%s' that is not instance of field '%s' in '%s'",
+ ClassHelper(reg->GetClass()).GetDescriptor(),
+ ClassHelper(field_class).GetDescriptor(),
+ ClassHelper(f->GetDeclaringClass()).GetDescriptor());
+ return false;
+ }
+ }
+ f->SetObj(obj, reg);
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles iput-quick, iput-wide-quick and iput-object-quick instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template<Primitive::Type field_type>
+static inline bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ // We lost the reference to the field index so we cannot get a more
+ // precised exception message.
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ return false;
+ }
+ MemberOffset field_offset(inst->VRegC_22c());
+ const bool is_volatile = false; // iput-x-quick only on non volatile fields.
+ const uint32_t vregA = inst->VRegA_22c(inst_data);
+ switch (field_type) {
+ case Primitive::kPrimInt:
+ obj->SetField32(field_offset, shadow_frame.GetVReg(vregA), is_volatile);
+ break;
+ case Primitive::kPrimLong:
+ obj->SetField64(field_offset, shadow_frame.GetVRegLong(vregA), is_volatile);
+ break;
+ case Primitive::kPrimNot:
+ obj->SetFieldObject(field_offset, shadow_frame.GetVRegReference(vregA), is_volatile);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ }
+ return true;
+}
+
+// Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
+// java.lang.String class is initialized.
+static inline String* ResolveString(Thread* self, MethodHelper& mh, uint32_t string_idx)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Class* java_lang_string_class = String::GetJavaLangString();
+ if (UNLIKELY(!java_lang_string_class->IsInitialized())) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (UNLIKELY(!class_linker->EnsureInitialized(java_lang_string_class,
+ true, true))) {
+ DCHECK(self->IsExceptionPending());
+ return NULL;
+ }
+ }
+ return mh.ResolveString(string_idx);
+}
+
+// Handles div-int, div-int/2addr, div-int/li16 and div-int/lit8 instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoIntDivide(ShadowFrame& shadow_frame, size_t result_reg,
+ int32_t dividend, int32_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int32_t kMinInt = std::numeric_limits<int32_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
+ shadow_frame.SetVReg(result_reg, kMinInt);
+ } else {
+ shadow_frame.SetVReg(result_reg, dividend / divisor);
+ }
+ return true;
+}
+
+// Handles rem-int, rem-int/2addr, rem-int/li16 and rem-int/lit8 instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoIntRemainder(ShadowFrame& shadow_frame, size_t result_reg,
+ int32_t dividend, int32_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int32_t kMinInt = std::numeric_limits<int32_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinInt && divisor == -1)) {
+ shadow_frame.SetVReg(result_reg, 0);
+ } else {
+ shadow_frame.SetVReg(result_reg, dividend % divisor);
+ }
+ return true;
+}
+
+// Handles div-long and div-long-2addr instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoLongDivide(ShadowFrame& shadow_frame, size_t result_reg,
+ int64_t dividend, int64_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int64_t kMinLong = std::numeric_limits<int64_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
+ shadow_frame.SetVRegLong(result_reg, kMinLong);
+ } else {
+ shadow_frame.SetVRegLong(result_reg, dividend / divisor);
+ }
+ return true;
+}
+
+// Handles rem-long and rem-long-2addr instructions.
+// Returns true on success, otherwise throws a java.lang.ArithmeticException and return false.
+static inline bool DoLongRemainder(ShadowFrame& shadow_frame, size_t result_reg,
+ int64_t dividend, int64_t divisor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const int64_t kMinLong = std::numeric_limits<int64_t>::min();
+ if (UNLIKELY(divisor == 0)) {
+ ThrowArithmeticExceptionDivideByZero();
+ return false;
+ }
+ if (UNLIKELY(dividend == kMinLong && divisor == -1)) {
+ shadow_frame.SetVRegLong(result_reg, 0);
+ } else {
+ shadow_frame.SetVRegLong(result_reg, dividend % divisor);
+ }
+ return true;
+}
+
+// Handles filled-new-array and filled-new-array-range instructions.
+// Returns true on success, otherwise throws an exception and returns false.
+template <bool is_range, bool do_access_check>
+bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame,
+ Thread* self, JValue* result);
+
+// Handles packed-switch instruction.
+// Returns the branch offset to the next instruction to execute.
+static inline int32_t DoPackedSwitch(const Instruction* inst, const ShadowFrame& shadow_frame,
+ uint16_t inst_data)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(inst->Opcode() == Instruction::PACKED_SWITCH);
+ const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t(inst_data));
+ DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kPackedSwitchSignature));
+ uint16_t size = switch_data[1];
+ DCHECK_GT(size, 0);
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
+ DCHECK(IsAligned<4>(keys));
+ int32_t first_key = keys[0];
+ const int32_t* targets = reinterpret_cast<const int32_t*>(&switch_data[4]);
+ DCHECK(IsAligned<4>(targets));
+ int32_t index = test_val - first_key;
+ if (index >= 0 && index < size) {
+ return targets[index];
+ } else {
+ // No corresponding value: move forward by 3 (size of PACKED_SWITCH).
+ return 3;
+ }
+}
+
+// Handles sparse-switch instruction.
+// Returns the branch offset to the next instruction to execute.
+static inline int32_t DoSparseSwitch(const Instruction* inst, const ShadowFrame& shadow_frame,
+ uint16_t inst_data)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(inst->Opcode() == Instruction::SPARSE_SWITCH);
+ const uint16_t* switch_data = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ int32_t test_val = shadow_frame.GetVReg(inst->VRegA_31t(inst_data));
+ DCHECK_EQ(switch_data[0], static_cast<uint16_t>(Instruction::kSparseSwitchSignature));
+ uint16_t size = switch_data[1];
+ DCHECK_GT(size, 0);
+ const int32_t* keys = reinterpret_cast<const int32_t*>(&switch_data[2]);
+ DCHECK(IsAligned<4>(keys));
+ const int32_t* entries = keys + size;
+ DCHECK(IsAligned<4>(entries));
+ int lo = 0;
+ int hi = size - 1;
+ while (lo <= hi) {
+ int mid = (lo + hi) / 2;
+ int32_t foundVal = keys[mid];
+ if (test_val < foundVal) {
+ hi = mid - 1;
+ } else if (test_val > foundVal) {
+ lo = mid + 1;
+ } else {
+ return entries[mid];
+ }
+ }
+ // No corresponding value: move forward by 3 (size of SPARSE_SWITCH).
+ return 3;
+}
+
+static inline uint32_t FindNextInstructionFollowingException(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ mirror::Object* this_object,
+ const instrumentation::Instrumentation* instrumentation)
+ ALWAYS_INLINE;
+
+static inline uint32_t FindNextInstructionFollowingException(Thread* self,
+ ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ mirror::Object* this_object,
+ const instrumentation::Instrumentation* instrumentation)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ self->VerifyStack();
+ ThrowLocation throw_location;
+ mirror::Throwable* exception = self->GetException(&throw_location);
+ bool clear_exception = false;
+ uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(exception->GetClass(), dex_pc,
+ &clear_exception);
+ if (found_dex_pc == DexFile::kDexNoIndex) {
+ instrumentation->MethodUnwindEvent(self, this_object,
+ shadow_frame.GetMethod(), dex_pc);
+ } else {
+ instrumentation->ExceptionCaughtEvent(self, throw_location,
+ shadow_frame.GetMethod(),
+ found_dex_pc, exception);
+ if (clear_exception) {
+ self->ClearException();
+ }
+ }
+ return found_dex_pc;
+}
+
+static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
+ __attribute__((cold, noreturn, noinline));
+
+static void UnexpectedOpcode(const Instruction* inst, MethodHelper& mh)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile());
+ exit(0); // Unreachable, keep GCC happy.
+}
+
+static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst,
+ const uint32_t dex_pc, MethodHelper& mh)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ constexpr bool kTracing = false;
+ if (kTracing) {
+#define TRACE_LOG std::cerr
+ std::ostringstream oss;
+ oss << PrettyMethod(shadow_frame.GetMethod())
+ << StringPrintf("\n0x%x: ", dex_pc)
+ << inst->DumpString(&mh.GetDexFile()) << "\n";
+ for (size_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
+ uint32_t raw_value = shadow_frame.GetVReg(i);
+ Object* ref_value = shadow_frame.GetVRegReference(i);
+ oss << StringPrintf(" vreg%d=0x%08X", i, raw_value);
+ if (ref_value != NULL) {
+ if (ref_value->GetClass()->IsStringClass() &&
+ ref_value->AsString()->GetCharArray() != NULL) {
+ oss << "/java.lang.String \"" << ref_value->AsString()->ToModifiedUtf8() << "\"";
+ } else {
+ oss << "/" << PrettyTypeOf(ref_value);
+ }
+ }
+ }
+ TRACE_LOG << oss.str() << "\n";
+#undef TRACE_LOG
+ }
+}
+
+static inline bool IsBackwardBranch(int32_t branch_offset) {
+ return branch_offset <= 0;
+}
+
+// Explicitly instantiate all DoInvoke functions.
+#define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoInvoke<_type, _is_range, _do_check>(Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+
+#define EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(_type) \
+ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, false); \
+ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, false, true); \
+ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, false); \
+ EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, true, true);
+
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kStatic); // invoke-static/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kDirect); // invoke-direct/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kVirtual); // invoke-virtual/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kSuper); // invoke-super/range.
+EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL(kInterface); // invoke-interface/range.
+#undef EXPLICIT_DO_INVOKE_ALL_TEMPLATE_DECL
+#undef EXPLICIT_DO_INVOKE_TEMPLATE_DECL
+
+// Explicitly instantiate all DoFieldGet functions.
+#define EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, _do_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoFieldGet<_find_type, _field_type, _do_check>(Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data)
+
+#define EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(_find_type, _field_type) \
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, false); \
+ EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL(_find_type, _field_type, true);
+
+// iget-XXX
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimBoolean);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimByte);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimChar);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimShort);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimInt);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstancePrimitiveRead, Primitive::kPrimLong);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(InstanceObjectRead, Primitive::kPrimNot);
+
+// sget-XXX
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimBoolean);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimByte);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimChar);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimShort);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimInt);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticPrimitiveRead, Primitive::kPrimLong);
+EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot);
+
+#undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL
+#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
+
+// Explicitly instantiate all DoFieldPut functions.
+#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoFieldPut<_find_type, _field_type, _do_check>(Thread* self, const ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data)
+
+#define EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(_find_type, _field_type) \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, false); \
+ EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, true);
+
+// iput-XXX
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimBoolean);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimByte);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimChar);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimShort);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimInt);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstancePrimitiveWrite, Primitive::kPrimLong);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(InstanceObjectWrite, Primitive::kPrimNot);
+
+// sput-XXX
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimBoolean);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimByte);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimChar);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimShort);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimInt);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticPrimitiveWrite, Primitive::kPrimLong);
+EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot);
+
+#undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL
+#undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL
+
+// Explicitly instantiate all DoInvokeVirtualQuick functions.
+#define EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(_is_range) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoInvokeVirtualQuick<_is_range>(Thread* self, ShadowFrame& shadow_frame, \
+ const Instruction* inst, uint16_t inst_data, \
+ JValue* result)
+
+EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(false); // invoke-virtual-quick.
+EXPLICIT_DO_INVOKE_VIRTUAL_QUICK_TEMPLATE_DECL(true); // invoke-virtual-quick-range.
+#undef EXPLICIT_INSTANTIATION_DO_INVOKE_VIRTUAL_QUICK
+
+// Explicitly instantiate all DoIGetQuick functions.
+#define EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(_field_type) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoIGetQuick<_field_type>(ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data)
+
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
+EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+#undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL
+
+// Explicitly instantiate all DoIPutQuick functions.
+#define EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(_field_type) \
+ template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE \
+ static bool DoIPutQuick<_field_type>(const ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data)
+
+EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimInt); // iget-quick.
+EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-quick.
+EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick.
+#undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
+
+} // namespace interpreter
+} // namespace art
+
+#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_COMMON_H_
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
new file mode 100644
index 0000000000..aa6bcd696f
--- /dev/null
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -0,0 +1,2400 @@
+/*
+ * Copyright (C) 2012 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 "interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+// In the following macros, we expect the following local variables exist:
+// - "self": the current Thread*.
+// - "inst" : the current Instruction*.
+// - "inst_data" : the current instruction's first 16 bits.
+// - "dex_pc": the current pc.
+// - "shadow_frame": the current shadow frame.
+// - "mh": the current MethodHelper.
+// - "currentHandlersTable": the current table of pointer to each instruction handler.
+
+// Advance to the next instruction and updates interpreter state.
+#define ADVANCE(_offset) \
+ do { \
+ int32_t disp = static_cast<int32_t>(_offset); \
+ inst = inst->RelativeAt(disp); \
+ dex_pc = static_cast<uint32_t>(static_cast<int32_t>(dex_pc) + disp); \
+ shadow_frame.SetDexPC(dex_pc); \
+ TraceExecution(shadow_frame, inst, dex_pc, mh); \
+ inst_data = inst->Fetch16(0); \
+ goto *currentHandlersTable[inst->Opcode(inst_data)]; \
+ } while (false)
+
+#define HANDLE_PENDING_EXCEPTION() goto exception_pending_label
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _offset) \
+ do { \
+ if (UNLIKELY(_is_exception_pending)) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ } else { \
+ ADVANCE(_offset); \
+ } \
+ } while (false)
+
+#define UPDATE_HANDLER_TABLE() \
+ currentHandlersTable = handlersTable[Runtime::Current()->GetInstrumentation()->GetInterpreterHandlerTable()]
+
+#define UNREACHABLE_CODE_CHECK() \
+ do { \
+ if (kIsDebugBuild) { \
+ LOG(FATAL) << "We should not be here !"; \
+ } \
+ } while (false)
+
+#define HANDLE_INSTRUCTION_START(opcode) op_##opcode: // NOLINT(whitespace/labels)
+#define HANDLE_INSTRUCTION_END() UNREACHABLE_CODE_CHECK()
+
+/**
+ * Interpreter based on computed goto tables.
+ *
+ * Each instruction is associated to a handler. This handler is responsible for executing the
+ * instruction and jump to the next instruction's handler.
+ * In order to limit the cost of instrumentation, we have two handler tables:
+ * - the "main" handler table: it contains handlers for normal execution of each instruction without
+ * handling of instrumentation.
+ * - the "alternative" handler table: it contains alternative handlers which first handle
+ * instrumentation before jumping to the corresponding "normal" instruction's handler.
+ *
+ * When instrumentation is active, the interpreter uses the "alternative" handler table. Otherwise
+ * it uses the "main" handler table.
+ *
+ * The current handler table is the handler table being used by the interpreter. It is updated:
+ * - on backward branch (goto, if and switch instructions)
+ * - after invoke
+ * - when an exception is thrown.
+ * This allows to support an attaching debugger to an already running application for instance.
+ *
+ * For a fast handler table update, handler tables are stored in an array of handler tables. Each
+ * handler table is represented by the InterpreterHandlerTable enum which allows to associate it
+ * to an index in this array of handler tables ((see Instrumentation::GetInterpreterHandlerTable).
+ *
+ * Here's the current layout of this array of handler tables:
+ *
+ * ---------------------+---------------+
+ * | NOP | (handler for NOP instruction)
+ * +---------------+
+ * "main" | MOVE | (handler for MOVE instruction)
+ * handler table +---------------+
+ * | ... |
+ * +---------------+
+ * | UNUSED_FF | (handler for UNUSED_FF instruction)
+ * ---------------------+---------------+
+ * | NOP | (alternative handler for NOP instruction)
+ * +---------------+
+ * "alternative" | MOVE | (alternative handler for MOVE instruction)
+ * handler table +---------------+
+ * | ... |
+ * +---------------+
+ * | UNUSED_FF | (alternative handler for UNUSED_FF instruction)
+ * ---------------------+---------------+
+ *
+ */
+template<bool do_access_check>
+JValue ExecuteGotoImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register) {
+ // Define handler tables:
+ // - The main handler table contains execution handlers for each instruction.
+ // - The alternative handler table contains prelude handlers which check for thread suspend and
+ // manage instrumentation before jumping to the execution handler.
+ static const void* const handlersTable[instrumentation::kNumHandlerTables][kNumPackedOpcodes] = {
+ {
+ // Main handler table.
+#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&op_##code,
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_HANDLER
+ }, {
+ // Alternative handler table.
+#define INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) &&alt_op_##code,
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_HANDLER
+ }
+ };
+
+ const bool do_assignability_check = do_access_check;
+ if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
+ LOG(FATAL) << "Invalid shadow frame for interpreter use";
+ return JValue();
+ }
+ self->VerifyStack();
+
+ uint32_t dex_pc = shadow_frame.GetDexPC();
+ const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
+ uint16_t inst_data;
+ const void* const* currentHandlersTable;
+ UPDATE_HANDLER_TABLE();
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
+ instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), 0);
+ }
+ }
+
+ // Jump to first instruction.
+ ADVANCE(0);
+ UNREACHABLE_CODE_CHECK();
+
+ HANDLE_INSTRUCTION_START(NOP)
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_FROM16)
+ shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_16)
+ shadow_frame.SetVReg(inst->VRegA_32x(),
+ shadow_frame.GetVReg(inst->VRegB_32x()));
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_WIDE)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_WIDE_FROM16)
+ shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_22x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_WIDE_16)
+ shadow_frame.SetVRegLong(inst->VRegA_32x(),
+ shadow_frame.GetVRegLong(inst->VRegB_32x()));
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_OBJECT)
+ shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_OBJECT_FROM16)
+ shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_22x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_OBJECT_16)
+ shadow_frame.SetVRegReference(inst->VRegA_32x(),
+ shadow_frame.GetVRegReference(inst->VRegB_32x()));
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_RESULT)
+ shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_RESULT_WIDE)
+ shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_RESULT_OBJECT)
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MOVE_EXCEPTION) {
+ Throwable* exception = self->GetException(NULL);
+ self->ClearException();
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_VOID) {
+ JValue result;
+ if (do_access_check) {
+ // If access checks are required then the dex-to-dex compiler and analysis of
+ // whether the class has final fields hasn't been performed. Conservatively
+ // perform the memory barrier now.
+ ANDROID_MEMBAR_STORE();
+ }
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_VOID_BARRIER) {
+ ANDROID_MEMBAR_STORE();
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN) {
+ JValue result;
+ result.SetJ(0);
+ result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_WIDE) {
+ JValue result;
+ result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RETURN_OBJECT) {
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ result.SetJ(0);
+ result.SetL(obj_result);
+ if (do_assignability_check && obj_result != NULL) {
+ Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
+ if (return_type == NULL) {
+ // Return the pending exception.
+ HANDLE_PENDING_EXCEPTION();
+ }
+ if (!obj_result->VerifierInstanceOf(return_type)) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Returning '%s' that is not instance of return type '%s'",
+ ClassHelper(obj_result->GetClass()).GetDescriptor(),
+ ClassHelper(return_type).GetDescriptor());
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc,
+ result);
+ }
+ return result;
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_4) {
+ uint32_t dst = inst->VRegA_11n(inst_data);
+ int32_t val = inst->VRegB_11n(inst_data);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_16) {
+ uint32_t dst = inst->VRegA_21s(inst_data);
+ int32_t val = inst->VRegB_21s();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST) {
+ uint32_t dst = inst->VRegA_31i(inst_data);
+ int32_t val = inst->VRegB_31i();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_HIGH16) {
+ uint32_t dst = inst->VRegA_21h(inst_data);
+ int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE_16)
+ shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE_32)
+ shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
+ ADVANCE(3);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE)
+ shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
+ ADVANCE(5);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_WIDE_HIGH16)
+ shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
+ static_cast<uint64_t>(inst->VRegB_21h()) << 48);
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_STRING) {
+ String* s = ResolveString(self, mh, inst->VRegB_21c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_STRING_JUMBO) {
+ String* s = ResolveString(self, mh, inst->VRegB_31c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+ ADVANCE(3);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CONST_CLASS) {
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MONITOR_ENTER) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorEnter(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MONITOR_EXIT) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorExit(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), 1);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CHECK_CAST) {
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+ if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
+ ThrowClassCastException(c, obj->GetClass());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ ADVANCE(2);
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INSTANCE_OF) {
+ Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ARRAY_LENGTH) {
+ Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+ if (UNLIKELY(array == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
+ ADVANCE(1);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEW_INSTANCE) {
+ Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEW_ARRAY) {
+ int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
+ Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ length, self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY) {
+ bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FILLED_NEW_ARRAY_RANGE) {
+ bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FILL_ARRAY_DATA) {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Array* array = obj->AsArray();
+ DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
+ const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+ if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
+ self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+ "Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "failed FILL_ARRAY_DATA; length=%d, index=%d",
+ array->GetLength(), payload->element_count);
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ uint32_t size_in_bytes = payload->element_count * payload->element_width;
+ memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes);
+ ADVANCE(3);
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(THROW) {
+ Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(exception == NULL)) {
+ ThrowNullPointerException(NULL, "throw with null exception");
+ } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Throwing '%s' that is not instance of Throwable",
+ ClassHelper(exception->GetClass()).GetDescriptor());
+ } else {
+ self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
+ }
+ HANDLE_PENDING_EXCEPTION();
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(GOTO) {
+ int8_t offset = inst->VRegA_10t(inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(GOTO_16) {
+ int16_t offset = inst->VRegA_20t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(GOTO_32) {
+ int32_t offset = inst->VRegA_30t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(PACKED_SWITCH) {
+ int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPARSE_SWITCH) {
+ int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPL_FLOAT) {
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPG_FLOAT) {
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPL_DOUBLE) {
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMPG_DOUBLE) {
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(CMP_LONG) {
+ int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
+ int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ ADVANCE(2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_EQ) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_NE) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LT) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GE) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GT) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LE) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_EQZ) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_NEZ) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LTZ) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GEZ) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_GTZ) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IF_LEZ) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ }
+ ADVANCE(offset);
+ } else {
+ ADVANCE(2);
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_BOOLEAN) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_BYTE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_CHAR) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_SHORT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_WIDE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AGET_OBJECT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_BOOLEAN) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_BYTE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_CHAR) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_SHORT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_WIDE) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(APUT_OBJECT) {
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) {
+ array->SetWithoutChecks(index, val);
+ ADVANCE(2);
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_BOOLEAN) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_BYTE) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_CHAR) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_SHORT) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_WIDE) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_OBJECT) {
+ bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_WIDE_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IGET_OBJECT_QUICK) {
+ bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_BOOLEAN) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_BYTE) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_CHAR) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_SHORT) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_WIDE) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SGET_OBJECT) {
+ bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_BOOLEAN) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_BYTE) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_CHAR) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_SHORT) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_WIDE) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_OBJECT) {
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_WIDE_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(IPUT_OBJECT_QUICK) {
+ bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_BOOLEAN) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_BYTE) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_CHAR) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_SHORT) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_WIDE) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SPUT_OBJECT) {
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL) {
+ bool success = DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE) {
+ bool success = DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_SUPER) {
+ bool success = DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_SUPER_RANGE) {
+ bool success = DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_DIRECT) {
+ bool success = DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_DIRECT_RANGE) {
+ bool success = DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_INTERFACE) {
+ bool success = DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_INTERFACE_RANGE) {
+ bool success = DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_STATIC) {
+ bool success = DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_STATIC_RANGE) {
+ bool success = DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_QUICK) {
+ bool success = DoInvokeVirtualQuick<false>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INVOKE_VIRTUAL_RANGE_QUICK) {
+ bool success = DoInvokeVirtualQuick<true>(self, shadow_frame, inst, inst_data, &result_register);
+ UPDATE_HANDLER_TABLE();
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 3);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_INT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NOT_INT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NOT_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(NEG_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(LONG_TO_INT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(LONG_TO_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(LONG_TO_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FLOAT_TO_INT) {
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, float>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FLOAT_TO_LONG) {
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, float>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(FLOAT_TO_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DOUBLE_TO_INT) {
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, double>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DOUBLE_TO_LONG) {
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, double>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DOUBLE_TO_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_BYTE)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_CHAR)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(INT_TO_SHORT)
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) +
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) -
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) *
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) &
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) |
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT)
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) ^
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) +
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) -
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) *
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_LONG) {
+ bool success = DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_LONG) {
+ bool success = DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) &
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) |
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_LONG)
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_FLOAT)
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
+ shadow_frame.GetVRegFloat(inst->VRegC_23x())));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_DOUBLE)
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
+ shadow_frame.GetVRegDouble(inst->VRegC_23x())));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) +
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) -
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) *
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) &
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) |
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) ^
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) +
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) -
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) *
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) &
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) |
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) ^
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_LONG_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) +
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) -
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) *
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) /
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_FLOAT_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ fmodf(shadow_frame.GetVRegFloat(vregA),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) +
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SUB_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) -
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) *
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) /
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_DOUBLE_2ADDR) {
+ uint32_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ fmod(shadow_frame.GetVRegDouble(vregA),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
+ ADVANCE(1);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) +
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RSUB_INT)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ inst->VRegC_22s() -
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) *
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT_LIT16) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT_LIT16) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT_LIT16)
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
+ inst->VRegC_22s());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(ADD_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) +
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(RSUB_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ inst->VRegC_22b() -
+ shadow_frame.GetVReg(inst->VRegB_22b()));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(MUL_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) *
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(DIV_INT_LIT8) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(REM_INT_LIT8) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, 2);
+ }
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(AND_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) &
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(OR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) |
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(XOR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) ^
+ inst->VRegC_22b());
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHL_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) <<
+ (inst->VRegC_22b() & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(SHR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) >>
+ (inst->VRegC_22b() & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(USHR_INT_LIT8)
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
+ (inst->VRegC_22b() & 0x1f));
+ ADVANCE(2);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_3E)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_3F)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_40)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_41)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_42)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_43)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_79)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_7A)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_EB)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_EC)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_ED)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_EE)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_EF)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F0)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F1)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F2)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F3)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F4)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F5)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F6)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F7)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F8)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_F9)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FA)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FB)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FC)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FD)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FE)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ HANDLE_INSTRUCTION_START(UNUSED_FF)
+ UnexpectedOpcode(inst, mh);
+ HANDLE_INSTRUCTION_END();
+
+ exception_pending_label: {
+ CHECK(self->IsExceptionPending());
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ UPDATE_HANDLER_TABLE();
+ }
+ Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_);
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, dex_pc,
+ this_object,
+ instrumentation);
+ if (found_dex_pc == DexFile::kDexNoIndex) {
+ return JValue(); /* Handled in caller. */
+ } else {
+ int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc);
+ ADVANCE(displacement);
+ }
+ }
+
+ // Create alternative instruction handlers dedicated to instrumentation.
+#define INSTRUMENTATION_INSTRUCTION_HANDLER(o, code, n, f, r, i, a, v) \
+ alt_op_##code: { \
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); \
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
+ instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \
+ shadow_frame.GetMethod(), dex_pc); \
+ } \
+ UPDATE_HANDLER_TABLE(); \
+ goto *handlersTable[instrumentation::kMainHandlerTable][Instruction::code]; \
+ }
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUMENTATION_INSTRUCTION_HANDLER)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUMENTATION_INSTRUCTION_HANDLER
+} // NOLINT(readability/fn_size)
+
+// Explicit definitions of ExecuteGotoImpl.
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteGotoImpl<true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteGotoImpl<false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
new file mode 100644
index 0000000000..bd0d87eefc
--- /dev/null
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -0,0 +1,2147 @@
+/*
+ * Copyright (C) 2012 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 "interpreter_common.h"
+
+namespace art {
+namespace interpreter {
+
+#define HANDLE_PENDING_EXCEPTION() \
+ do { \
+ CHECK(self->IsExceptionPending()); \
+ if (UNLIKELY(self->TestAllFlags())) { \
+ CheckSuspend(self); \
+ } \
+ Object* this_object = shadow_frame.GetThisObject(code_item->ins_size_); \
+ uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame, \
+ inst->GetDexPc(insns), \
+ this_object, \
+ instrumentation); \
+ if (found_dex_pc == DexFile::kDexNoIndex) { \
+ return JValue(); /* Handled in caller. */ \
+ } else { \
+ int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc); \
+ inst = inst->RelativeAt(displacement); \
+ } \
+ } while (false)
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _next_function) \
+ do { \
+ if (UNLIKELY(_is_exception_pending)) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ } else { \
+ inst = inst->_next_function(); \
+ } \
+ } while (false)
+
+// Code to run before each dex instruction.
+#define PREAMBLE()
+
+template<bool do_access_check>
+JValue ExecuteSwitchImpl(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register) {
+ bool do_assignability_check = do_access_check;
+ if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
+ LOG(FATAL) << "Invalid shadow frame for interpreter use";
+ return JValue();
+ }
+ self->VerifyStack();
+
+ uint32_t dex_pc = shadow_frame.GetDexPC();
+ const instrumentation::Instrumentation* const instrumentation = Runtime::Current()->GetInstrumentation();
+ if (LIKELY(dex_pc == 0)) { // We are entering the method as opposed to deoptimizing..
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
+ instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), 0);
+ }
+ }
+ const uint16_t* const insns = code_item->insns_;
+ const Instruction* inst = Instruction::At(insns + dex_pc);
+ uint16_t inst_data;
+ while (true) {
+ dex_pc = inst->GetDexPc(insns);
+ shadow_frame.SetDexPC(dex_pc);
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) {
+ instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), dex_pc);
+ }
+ TraceExecution(shadow_frame, inst, dex_pc, mh);
+ inst_data = inst->Fetch16(0);
+ switch (inst->Opcode(inst_data)) {
+ case Instruction::NOP:
+ PREAMBLE();
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_FROM16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MOVE_16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_32x(),
+ shadow_frame.GetVReg(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::MOVE_WIDE:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_WIDE_FROM16:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MOVE_WIDE_16:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_32x(),
+ shadow_frame.GetVRegLong(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::MOVE_OBJECT:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_OBJECT_FROM16:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MOVE_OBJECT_16:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_32x(),
+ shadow_frame.GetVRegReference(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::MOVE_RESULT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_RESULT_WIDE:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_RESULT_OBJECT:
+ PREAMBLE();
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::MOVE_EXCEPTION: {
+ PREAMBLE();
+ Throwable* exception = self->GetException(NULL);
+ self->ClearException();
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::RETURN_VOID: {
+ PREAMBLE();
+ JValue result;
+ if (do_access_check) {
+ // If access checks are required then the dex-to-dex compiler and analysis of
+ // whether the class has final fields hasn't been performed. Conservatively
+ // perform the memory barrier now.
+ ANDROID_MEMBAR_STORE();
+ }
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN_VOID_BARRIER: {
+ PREAMBLE();
+ ANDROID_MEMBAR_STORE();
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN: {
+ PREAMBLE();
+ JValue result;
+ result.SetJ(0);
+ result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN_WIDE: {
+ PREAMBLE();
+ JValue result;
+ result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::RETURN_OBJECT: {
+ PREAMBLE();
+ JValue result;
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ Object* obj_result = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ result.SetJ(0);
+ result.SetL(obj_result);
+ if (do_assignability_check && obj_result != NULL) {
+ Class* return_type = MethodHelper(shadow_frame.GetMethod()).GetReturnType();
+ if (return_type == NULL) {
+ // Return the pending exception.
+ HANDLE_PENDING_EXCEPTION();
+ }
+ if (!obj_result->VerifierInstanceOf(return_type)) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Returning '%s' that is not instance of return type '%s'",
+ ClassHelper(obj_result->GetClass()).GetDescriptor(),
+ ClassHelper(return_type).GetDescriptor());
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(), inst->GetDexPc(insns),
+ result);
+ }
+ return result;
+ }
+ case Instruction::CONST_4: {
+ PREAMBLE();
+ uint4_t dst = inst->VRegA_11n(inst_data);
+ int4_t val = inst->VRegB_11n(inst_data);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::CONST_16: {
+ PREAMBLE();
+ uint8_t dst = inst->VRegA_21s(inst_data);
+ int16_t val = inst->VRegB_21s();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CONST: {
+ PREAMBLE();
+ uint8_t dst = inst->VRegA_31i(inst_data);
+ int32_t val = inst->VRegB_31i();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_3xx();
+ break;
+ }
+ case Instruction::CONST_HIGH16: {
+ PREAMBLE();
+ uint8_t dst = inst->VRegA_21h(inst_data);
+ int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, NULL);
+ }
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CONST_WIDE_16:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::CONST_WIDE_32:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
+ inst = inst->Next_3xx();
+ break;
+ case Instruction::CONST_WIDE:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
+ inst = inst->Next_51l();
+ break;
+ case Instruction::CONST_WIDE_HIGH16:
+ shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
+ static_cast<uint64_t>(inst->VRegB_21h()) << 48);
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::CONST_STRING: {
+ PREAMBLE();
+ String* s = ResolveString(self, mh, inst->VRegB_21c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::CONST_STRING_JUMBO: {
+ PREAMBLE();
+ String* s = ResolveString(self, mh, inst->VRegB_31c());
+ if (UNLIKELY(s == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+ inst = inst->Next_3xx();
+ }
+ break;
+ }
+ case Instruction::CONST_CLASS: {
+ PREAMBLE();
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::MONITOR_ENTER: {
+ PREAMBLE();
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorEnter(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+ break;
+ }
+ case Instruction::MONITOR_EXIT: {
+ PREAMBLE();
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorExit(self, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+ break;
+ }
+ case Instruction::CHECK_CAST: {
+ PREAMBLE();
+ Class* c = ResolveVerifyAndClinit(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+ if (UNLIKELY(obj != NULL && !obj->InstanceOf(c))) {
+ ThrowClassCastException(c, obj->GetClass());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ inst = inst->Next_2xx();
+ }
+ }
+ break;
+ }
+ case Instruction::INSTANCE_OF: {
+ PREAMBLE();
+ Class* c = ResolveVerifyAndClinit(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ self, false, do_access_check);
+ if (UNLIKELY(c == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ shadow_frame.SetVReg(inst->VRegA_22c(inst_data), (obj != NULL && obj->InstanceOf(c)) ? 1 : 0);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::ARRAY_LENGTH: {
+ PREAMBLE();
+ Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+ if (UNLIKELY(array == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
+ inst = inst->Next_1xx();
+ }
+ break;
+ }
+ case Instruction::NEW_INSTANCE: {
+ PREAMBLE();
+ Object* obj = AllocObjectFromCodeInstrumented(inst->VRegB_21c(), shadow_frame.GetMethod(),
+ self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::NEW_ARRAY: {
+ PREAMBLE();
+ int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
+ Object* obj = AllocArrayFromCodeInstrumented(inst->VRegC_22c(), shadow_frame.GetMethod(),
+ length, self, do_access_check);
+ if (UNLIKELY(obj == NULL)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::FILLED_NEW_ARRAY: {
+ PREAMBLE();
+ bool success = DoFilledNewArray<false, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::FILLED_NEW_ARRAY_RANGE: {
+ PREAMBLE();
+ bool success = DoFilledNewArray<true, do_access_check>(inst, shadow_frame,
+ self, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::FILL_ARRAY_DATA: {
+ PREAMBLE();
+ Object* obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+ if (UNLIKELY(obj == NULL)) {
+ ThrowNullPointerException(NULL, "null array in FILL_ARRAY_DATA");
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ Array* array = obj->AsArray();
+ DCHECK(array->IsArrayInstance() && !array->IsObjectArray());
+ const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+ if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
+ self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
+ "Ljava/lang/ArrayIndexOutOfBoundsException;",
+ "failed FILL_ARRAY_DATA; length=%d, index=%d",
+ array->GetLength(), payload->element_count);
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ uint32_t size_in_bytes = payload->element_count * payload->element_width;
+ memcpy(array->GetRawData(payload->element_width), payload->data, size_in_bytes);
+ inst = inst->Next_3xx();
+ break;
+ }
+ case Instruction::THROW: {
+ PREAMBLE();
+ Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(exception == NULL)) {
+ ThrowNullPointerException(NULL, "throw with null exception");
+ } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+ // This should never happen.
+ self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
+ "Ljava/lang/VirtualMachineError;",
+ "Throwing '%s' that is not instance of Throwable",
+ ClassHelper(exception->GetClass()).GetDescriptor());
+ } else {
+ self->SetException(shadow_frame.GetCurrentLocationForThrow(), exception->AsThrowable());
+ }
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ case Instruction::GOTO: {
+ PREAMBLE();
+ int8_t offset = inst->VRegA_10t(inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::GOTO_16: {
+ PREAMBLE();
+ int16_t offset = inst->VRegA_20t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::GOTO_32: {
+ PREAMBLE();
+ int32_t offset = inst->VRegA_30t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::PACKED_SWITCH: {
+ PREAMBLE();
+ int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::SPARSE_SWITCH: {
+ PREAMBLE();
+ int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ break;
+ }
+ case Instruction::CMPL_FLOAT: {
+ PREAMBLE();
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CMPG_FLOAT: {
+ PREAMBLE();
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CMPL_DOUBLE: {
+ PREAMBLE();
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+
+ case Instruction::CMPG_DOUBLE: {
+ PREAMBLE();
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::CMP_LONG: {
+ PREAMBLE();
+ int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
+ int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ break;
+ }
+ case Instruction::IF_EQ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) == shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_NE: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) != shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LT: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) < shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GE: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GT: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) > shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LE: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <= shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_EQZ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_NEZ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LTZ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GEZ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_GTZ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::IF_LEZ: {
+ PREAMBLE();
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
+ int16_t offset = inst->VRegB_21t();
+ if (IsBackwardBranch(offset)) {
+ if (UNLIKELY(self->TestAllFlags())) {
+ CheckSuspend(self);
+ }
+ }
+ inst = inst->RelativeAt(offset);
+ } else {
+ inst = inst->Next_2xx();
+ }
+ break;
+ }
+ case Instruction::AGET_BOOLEAN: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_BYTE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_CHAR: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_SHORT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_WIDE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetData()[index]);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::AGET_OBJECT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index))) {
+ shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_BOOLEAN: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ BooleanArray* array = a->AsBooleanArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_BYTE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ByteArray* array = a->AsByteArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_CHAR: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ CharArray* array = a->AsCharArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_SHORT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ShortArray* array = a->AsShortArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ IntArray* array = a->AsIntArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_WIDE: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ LongArray* array = a->AsLongArray();
+ if (LIKELY(array->IsValidIndex(index))) {
+ array->GetData()[index] = val;
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::APUT_OBJECT: {
+ PREAMBLE();
+ Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == NULL)) {
+ ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+ HANDLE_PENDING_EXCEPTION();
+ break;
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ Object* val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+ ObjectArray<Object>* array = a->AsObjectArray<Object>();
+ if (LIKELY(array->IsValidIndex(index) && array->CheckAssignable(val))) {
+ array->SetWithoutChecks(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ break;
+ }
+ case Instruction::IGET_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_WIDE_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IGET_OBJECT_QUICK: {
+ PREAMBLE();
+ bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SGET_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_WIDE_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::IPUT_OBJECT_QUICK: {
+ PREAMBLE();
+ bool success = DoIPutQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_BOOLEAN: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_BYTE: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_CHAR: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_SHORT: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_WIDE: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SPUT_OBJECT: {
+ PREAMBLE();
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL: {
+ PREAMBLE();
+ bool success = DoInvoke<kVirtual, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kVirtual, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_SUPER: {
+ PREAMBLE();
+ bool success = DoInvoke<kSuper, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_SUPER_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kSuper, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_DIRECT: {
+ PREAMBLE();
+ bool success = DoInvoke<kDirect, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_DIRECT_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kDirect, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_INTERFACE: {
+ PREAMBLE();
+ bool success = DoInvoke<kInterface, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_INTERFACE_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kInterface, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_STATIC: {
+ PREAMBLE();
+ bool success = DoInvoke<kStatic, false, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_STATIC_RANGE: {
+ PREAMBLE();
+ bool success = DoInvoke<kStatic, true, do_access_check>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL_QUICK: {
+ PREAMBLE();
+ bool success = DoInvokeVirtualQuick<false>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
+ PREAMBLE();
+ bool success = DoInvokeVirtualQuick<true>(self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ break;
+ }
+ case Instruction::NEG_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NOT_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NEG_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NOT_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NEG_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::NEG_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::LONG_TO_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::LONG_TO_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::LONG_TO_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::FLOAT_TO_INT: {
+ PREAMBLE();
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, float>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::FLOAT_TO_LONG: {
+ PREAMBLE();
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, float>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::FLOAT_TO_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::DOUBLE_TO_INT: {
+ PREAMBLE();
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, double>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DOUBLE_TO_LONG: {
+ PREAMBLE();
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, double>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DOUBLE_TO_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_BYTE:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int8_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_CHAR:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<uint16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::INT_TO_SHORT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ static_cast<int16_t>(shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ case Instruction::ADD_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) +
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) -
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) *
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_INT: {
+ PREAMBLE();
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::REM_INT: {
+ PREAMBLE();
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::SHL_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::USHR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::AND_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) &
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) |
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) ^
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) +
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) -
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) *
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_LONG:
+ PREAMBLE();
+ DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+ break;
+ case Instruction::REM_LONG:
+ PREAMBLE();
+ DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+ break;
+ case Instruction::AND_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) &
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) |
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHL_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::USHR_LONG:
+ PREAMBLE();
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::REM_FLOAT:
+ PREAMBLE();
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
+ shadow_frame.GetVRegFloat(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SUB_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::REM_DOUBLE:
+ PREAMBLE();
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
+ shadow_frame.GetVRegDouble(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) +
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) -
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) *
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+ break;
+ }
+ case Instruction::REM_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+ break;
+ }
+ case Instruction::SHL_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SHR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::USHR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::AND_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) &
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::OR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) |
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::XOR_INT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) ^
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) +
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) -
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) *
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ break;
+ }
+ case Instruction::REM_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ break;
+ }
+ case Instruction::AND_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) &
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::OR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) |
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::XOR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) ^
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SHL_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SHR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::USHR_LONG_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) +
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) -
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) *
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) /
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::REM_FLOAT_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ fmodf(shadow_frame.GetVRegFloat(vregA),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) +
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::SUB_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) -
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::MUL_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) *
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::DIV_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) /
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::REM_DOUBLE_2ADDR: {
+ PREAMBLE();
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ fmod(shadow_frame.GetVRegDouble(vregA),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ break;
+ }
+ case Instruction::ADD_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) +
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::RSUB_INT:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ inst->VRegC_22s() -
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) *
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_INT_LIT16: {
+ PREAMBLE();
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::REM_INT_LIT16: {
+ PREAMBLE();
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)), inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::AND_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_INT_LIT16:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::ADD_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) +
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::RSUB_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ inst->VRegC_22b() -
+ shadow_frame.GetVReg(inst->VRegB_22b()));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::MUL_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) *
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::DIV_INT_LIT8: {
+ PREAMBLE();
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::REM_INT_LIT8: {
+ PREAMBLE();
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ break;
+ }
+ case Instruction::AND_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) &
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::OR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) |
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::XOR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) ^
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHL_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) <<
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::SHR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) >>
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::USHR_INT_LIT8:
+ PREAMBLE();
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ break;
+ case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
+ case Instruction::UNUSED_EB ... Instruction::UNUSED_FF:
+ case Instruction::UNUSED_79:
+ case Instruction::UNUSED_7A:
+ UnexpectedOpcode(inst, mh);
+ }
+ }
+} // NOLINT(readability/fn_size)
+
+// Explicit definitions of ExecuteSwitchImpl.
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteSwitchImpl<true>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+template SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) HOT_ATTR
+JValue ExecuteSwitchImpl<false>(Thread* self, MethodHelper& mh,
+ const DexFile::CodeItem* code_item,
+ ShadowFrame& shadow_frame, JValue result_register);
+
+} // namespace interpreter
+} // namespace art
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index a2efc48c84..523d89278a 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -47,7 +47,7 @@ std::string DescribeMethod(const MethodId& method_id) {
std::string DescribeRefTypeId(const RefTypeId& ref_type_id) {
std::string signature("unknown");
- Dbg::GetSignature(ref_type_id, signature);
+ Dbg::GetSignature(ref_type_id, &signature);
return StringPrintf("%#llx (%s)", ref_type_id, signature.c_str());
}
@@ -547,7 +547,7 @@ static JdwpError RT_Signature(JdwpState*, Request& request, ExpandBuf* pReply, b
RefTypeId refTypeId = request.ReadRefTypeId();
std::string signature;
- JdwpError status = Dbg::GetSignature(refTypeId, signature);
+ JdwpError status = Dbg::GetSignature(refTypeId, &signature);
if (status != ERR_NONE) {
return status;
}
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 6a0990eec6..ec717c1bee 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -228,6 +228,7 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
const char* name, const char* sig, bool is_static)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Class* c = soa.Decode<Class*>(jni_class);
+ DCHECK(c != nullptr);
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
return NULL;
}
@@ -324,14 +325,14 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con
return soa.EncodeField(field);
}
-static void PinPrimitiveArray(const ScopedObjectAccess& soa, const Array* array)
+static void PinPrimitiveArray(const ScopedObjectAccess& soa, Array* array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
JavaVMExt* vm = soa.Vm();
MutexLock mu(soa.Self(), vm->pins_lock);
vm->pin_table.Add(array);
}
-static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, const Array* array)
+static void UnpinPrimitiveArray(const ScopedObjectAccess& soa, Array* array)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
JavaVMExt* vm = soa.Vm();
MutexLock mu(soa.Self(), vm->pins_lock);
@@ -450,7 +451,7 @@ class SharedLibrary {
class_loader_(class_loader),
jni_on_load_lock_("JNI_OnLoad lock"),
jni_on_load_cond_("JNI_OnLoad condition variable", jni_on_load_lock_),
- jni_on_load_thread_id_(Thread::Current()->GetThinLockId()),
+ jni_on_load_thread_id_(Thread::Current()->GetThreadId()),
jni_on_load_result_(kPending) {
}
@@ -475,7 +476,7 @@ class SharedLibrary {
{
MutexLock mu(self, jni_on_load_lock_);
- if (jni_on_load_thread_id_ == self->GetThinLockId()) {
+ if (jni_on_load_thread_id_ == self->GetThreadId()) {
// Check this so we don't end up waiting for ourselves. We need to return "true" so the
// caller can continue.
LOG(INFO) << *self << " recursive attempt to load library " << "\"" << path_ << "\"";
@@ -1999,7 +2000,7 @@ class JNI {
CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, java_string);
ScopedObjectAccess soa(env);
String* s = soa.Decode<String*>(java_string);
- const CharArray* chars = s->GetCharArray();
+ CharArray* chars = s->GetCharArray();
PinPrimitiveArray(soa, chars);
if (is_copy != NULL) {
*is_copy = JNI_FALSE;
@@ -2356,9 +2357,9 @@ class JNI {
for (jint i = 0; i < method_count; ++i) {
const char* name = methods[i].name;
const char* sig = methods[i].signature;
-
+ bool is_fast = false;
if (*sig == '!') {
- // TODO: fast jni. it's too noisy to log all these.
+ is_fast = true;
++sig;
}
@@ -2381,7 +2382,7 @@ class JNI {
VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
- m->RegisterNative(soa.Self(), methods[i].fnPtr);
+ m->RegisterNative(soa.Self(), methods[i].fnPtr, is_fast);
}
return JNI_OK;
}
@@ -3081,15 +3082,6 @@ void JavaVMExt::AllowNewWeakGlobals() {
weak_globals_add_condition_.Broadcast(self);
}
-void JavaVMExt::SweepWeakGlobals(IsMarkedTester is_marked, void* arg) {
- MutexLock mu(Thread::Current(), weak_globals_lock_);
- for (const Object** entry : weak_globals_) {
- if (!is_marked(*entry, arg)) {
- *entry = kClearedJniWeakGlobal;
- }
- }
-}
-
mirror::Object* JavaVMExt::DecodeWeakGlobal(Thread* self, IndirectRef ref) {
MutexLock mu(self, weak_globals_lock_);
while (UNLIKELY(!allow_new_weak_globals_)) {
@@ -3115,8 +3107,8 @@ void JavaVMExt::DumpReferenceTables(std::ostream& os) {
}
bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader,
- std::string& detail) {
- detail.clear();
+ std::string* detail) {
+ detail->clear();
// See if we've already loaded this library. If we have, and the class loader
// matches, return successfully without doing anything.
@@ -3134,7 +3126,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo
// The library will be associated with class_loader. The JNI
// spec says we can't load the same library into more than one
// class loader.
- StringAppendF(&detail, "Shared library \"%s\" already opened by "
+ StringAppendF(detail, "Shared library \"%s\" already opened by "
"ClassLoader %p; can't open in ClassLoader %p",
path.c_str(), library->GetClassLoader(), class_loader);
LOG(WARNING) << detail;
@@ -3143,7 +3135,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo
VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
<< "ClassLoader " << class_loader << "]";
if (!library->CheckOnLoadResult()) {
- StringAppendF(&detail, "JNI_OnLoad failed on a previous attempt "
+ StringAppendF(detail, "JNI_OnLoad failed on a previous attempt "
"to load \"%s\"", path.c_str());
return false;
}
@@ -3170,7 +3162,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo
VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
if (handle == NULL) {
- detail = dlerror();
+ *detail = dlerror();
LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << detail;
return false;
}
@@ -3220,9 +3212,9 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo
self->SetClassLoaderOverride(old_class_loader);
if (version == JNI_ERR) {
- StringAppendF(&detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
+ StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
} else if (IsBadJniVersion(version)) {
- StringAppendF(&detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
+ StringAppendF(detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
path.c_str(), version);
// It's unwise to call dlclose() here, but we can mark it
// as bad and ensure that future load attempts will fail.
@@ -3271,6 +3263,18 @@ void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) {
return native_method;
}
+void JavaVMExt::SweepJniWeakGlobals(RootVisitor visitor, void* arg) {
+ MutexLock mu(Thread::Current(), weak_globals_lock_);
+ for (mirror::Object** entry : weak_globals_) {
+ mirror::Object* obj = *entry;
+ mirror::Object* new_obj = visitor(obj, arg);
+ if (new_obj == nullptr) {
+ new_obj = kClearedJniWeakGlobal;
+ }
+ *entry = new_obj;
+ }
+}
+
void JavaVMExt::VisitRoots(RootVisitor* visitor, void* arg) {
Thread* self = Thread::Current();
{
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index 32d0bfcee8..888d5e5458 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -73,7 +73,7 @@ class JavaVMExt : public JavaVM {
* human-readable description of the error.
*/
bool LoadNativeLibrary(const std::string& path, mirror::ClassLoader* class_loader,
- std::string& detail)
+ std::string* detail)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/**
@@ -98,7 +98,7 @@ class JavaVMExt : public JavaVM {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DeleteWeakGlobalRef(Thread* self, jweak obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SweepWeakGlobals(IsMarkedTester is_marked, void* arg);
+ void SweepJniWeakGlobals(RootVisitor visitor, void* arg);
mirror::Object* DecodeWeakGlobal(Thread* self, IndirectRef ref);
Runtime* runtime;
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 79d156de27..c389580ebf 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -1012,31 +1012,50 @@ TEST_F(JniInternalTest, RegisterNatives) {
scalar_type, \
expected_class_descriptor) \
jsize size = 4; \
+ \
/* Allocate an array and check it has the right type and length. */ \
scalar_type ## Array a = env_->new_fn(size); \
EXPECT_TRUE(a != NULL); \
EXPECT_TRUE(env_->IsInstanceOf(a, env_->FindClass(expected_class_descriptor))); \
EXPECT_EQ(size, env_->GetArrayLength(a)); \
+ \
+ /* GetPrimitiveArrayRegion/SetPrimitiveArrayRegion */ \
/* AIOOBE for negative start offset. */ \
env_->get_region_fn(a, -1, 1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
env_->set_region_fn(a, -1, 1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
+ \
/* AIOOBE for negative length. */ \
env_->get_region_fn(a, 0, -1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
env_->set_region_fn(a, 0, -1, NULL); \
EXPECT_EXCEPTION(aioobe_); \
+ \
/* AIOOBE for buffer overrun. */ \
env_->get_region_fn(a, size - 1, size, NULL); \
EXPECT_EXCEPTION(aioobe_); \
env_->set_region_fn(a, size - 1, size, NULL); \
EXPECT_EXCEPTION(aioobe_); \
+ \
+ /* It's okay for the buffer to be NULL as long as the length is 0. */ \
+ env_->get_region_fn(a, 2, 0, NULL); \
+ /* Even if the offset is invalid... */ \
+ env_->get_region_fn(a, 123, 0, NULL); \
+ EXPECT_EXCEPTION(aioobe_); \
+ \
+ /* It's okay for the buffer to be NULL as long as the length is 0. */ \
+ env_->set_region_fn(a, 2, 0, NULL); \
+ /* Even if the offset is invalid... */ \
+ env_->set_region_fn(a, 123, 0, NULL); \
+ EXPECT_EXCEPTION(aioobe_); \
+ \
/* Prepare a couple of buffers. */ \
UniquePtr<scalar_type[]> src_buf(new scalar_type[size]); \
UniquePtr<scalar_type[]> dst_buf(new scalar_type[size]); \
for (jsize i = 0; i < size; ++i) { src_buf[i] = scalar_type(i); } \
for (jsize i = 0; i < size; ++i) { dst_buf[i] = scalar_type(-1); } \
+ \
/* Copy all of src_buf onto the heap. */ \
env_->set_region_fn(a, 0, size, &src_buf[0]); \
/* Copy back only part. */ \
@@ -1252,6 +1271,12 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) {
EXPECT_EQ('l', chars[2]);
EXPECT_EQ('x', chars[3]);
+ // It's okay for the buffer to be NULL as long as the length is 0.
+ env_->GetStringRegion(s, 2, 0, NULL);
+ // Even if the offset is invalid...
+ env_->GetStringRegion(s, 123, 0, NULL);
+ EXPECT_EXCEPTION(sioobe_);
+
env_->GetStringUTFRegion(s, -1, 0, NULL);
EXPECT_EXCEPTION(sioobe_);
env_->GetStringUTFRegion(s, 0, -1, NULL);
@@ -1267,6 +1292,12 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) {
EXPECT_EQ('e', bytes[1]);
EXPECT_EQ('l', bytes[2]);
EXPECT_EQ('x', bytes[3]);
+
+ // It's okay for the buffer to be NULL as long as the length is 0.
+ env_->GetStringUTFRegion(s, 2, 0, NULL);
+ // Even if the offset is invalid...
+ env_->GetStringUTFRegion(s, 123, 0, NULL);
+ EXPECT_EXCEPTION(sioobe_);
}
TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) {
diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h
new file mode 100644
index 0000000000..efd3d9d25e
--- /dev/null
+++ b/runtime/lock_word-inl.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_LOCK_WORD_INL_H_
+#define ART_RUNTIME_LOCK_WORD_INL_H_
+
+#include "lock_word.h"
+
+namespace art {
+
+inline uint32_t LockWord::ThinLockOwner() const {
+ DCHECK_EQ(GetState(), kThinLocked);
+ return (value_ >> kThinLockOwnerShift) & kThinLockOwnerMask;
+}
+
+inline uint32_t LockWord::ThinLockCount() const {
+ DCHECK_EQ(GetState(), kThinLocked);
+ return (value_ >> kThinLockCountShift) & kThinLockCountMask;
+}
+
+inline Monitor* LockWord::FatLockMonitor() const {
+ DCHECK_EQ(GetState(), kFatLocked);
+ return reinterpret_cast<Monitor*>(value_ << kStateSize);
+}
+
+inline LockWord::LockWord() : value_(0) {
+ DCHECK_EQ(GetState(), kUnlocked);
+}
+
+inline LockWord::LockWord(Monitor* mon)
+ : value_((reinterpret_cast<uint32_t>(mon) >> kStateSize) | (kStateFat << kStateShift)) {
+ DCHECK_EQ(FatLockMonitor(), mon);
+}
+
+inline int32_t LockWord::GetHashCode() const {
+ DCHECK_EQ(GetState(), kHashCode);
+ return (value_ >> kHashShift) & kHashMask;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_LOCK_WORD_INL_H_
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
new file mode 100644
index 0000000000..1882ae6504
--- /dev/null
+++ b/runtime/lock_word.h
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_LOCK_WORD_H_
+#define ART_RUNTIME_LOCK_WORD_H_
+
+#include <iosfwd>
+#include <stdint.h>
+
+#include "base/logging.h"
+
+namespace art {
+namespace mirror {
+ class Object;
+} // namespace mirror
+
+class Monitor;
+
+/* The lock value itself as stored in mirror::Object::monitor_. The two most significant bits of
+ * the state. The three possible states are fat locked, thin/unlocked, and hash code.
+ * When the lock word is in the "thin" state and its bits are formatted as follows:
+ *
+ * |33|22222222221111|1111110000000000|
+ * |10|98765432109876|5432109876543210|
+ * |00| lock count |thread id owner |
+ *
+ * When the lock word is in the "fat" state and its bits are formatted as follows:
+ *
+ * |33|222222222211111111110000000000|
+ * |10|987654321098765432109876543210|
+ * |01| Monitor* >> kStateSize |
+ *
+ * When the lock word is in hash state and its bits are formatted as follows:
+ *
+ * |33|222222222211111111110000000000|
+ * |10|987654321098765432109876543210|
+ * |10| HashCode |
+ */
+class LockWord {
+ public:
+ enum {
+ // Number of bits to encode the state, currently just fat or thin/unlocked or hash code.
+ kStateSize = 2,
+ // Number of bits to encode the thin lock owner.
+ kThinLockOwnerSize = 16,
+ // Remaining bits are the recursive lock count.
+ kThinLockCountSize = 32 - kThinLockOwnerSize - kStateSize,
+ // Thin lock bits. Owner in lowest bits.
+
+ kThinLockOwnerShift = 0,
+ kThinLockOwnerMask = (1 << kThinLockOwnerSize) - 1,
+ // Count in higher bits.
+ kThinLockCountShift = kThinLockOwnerSize + kThinLockOwnerShift,
+ kThinLockCountMask = (1 << kThinLockCountShift) - 1,
+ kThinLockMaxCount = kThinLockCountMask,
+
+ // State in the highest bits.
+ kStateShift = kThinLockCountSize + kThinLockCountShift,
+ kStateMask = (1 << kStateSize) - 1,
+ kStateThinOrUnlocked = 0,
+ kStateFat = 1,
+ kStateHash = 2,
+
+ // When the state is kHashCode, the non-state bits hold the hashcode.
+ kHashShift = 0,
+ kHashSize = 32 - kStateSize,
+ kHashMask = (1 << kHashSize) - 1,
+ };
+
+ static LockWord FromThinLockId(uint32_t thread_id, uint32_t count) {
+ CHECK_LE(thread_id, static_cast<uint32_t>(kThinLockOwnerMask));
+ return LockWord((thread_id << kThinLockOwnerShift) | (count << kThinLockCountShift) |
+ (kStateThinOrUnlocked << kStateShift));
+ }
+
+ static LockWord FromHashCode(uint32_t hash_code) {
+ CHECK_LE(hash_code, static_cast<uint32_t>(kHashMask));
+ return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift));
+ }
+
+ enum LockState {
+ kUnlocked, // No lock owners.
+ kThinLocked, // Single uncontended owner.
+ kFatLocked, // See associated monitor.
+ kHashCode, // Lock word contains an identity hash.
+ };
+
+ LockState GetState() const {
+ uint32_t internal_state = (value_ >> kStateShift) & kStateMask;
+ if (value_ == 0) {
+ return kUnlocked;
+ } else if (internal_state == kStateThinOrUnlocked) {
+ return kThinLocked;
+ } else if (internal_state == kStateHash) {
+ return kHashCode;
+ } else {
+ DCHECK_EQ(internal_state, static_cast<uint32_t>(kStateFat));
+ return kFatLocked;
+ }
+ }
+
+ // Return the owner thin lock thread id.
+ uint32_t ThinLockOwner() const;
+
+ // Return the number of times a lock value has been locked.
+ uint32_t ThinLockCount() const;
+
+ // Return the Monitor encoded in a fat lock.
+ Monitor* FatLockMonitor() const;
+
+ // Default constructor with no lock ownership.
+ LockWord();
+
+ // Constructor a lock word for inflation to use a Monitor.
+ explicit LockWord(Monitor* mon);
+
+ bool operator==(const LockWord& rhs) const {
+ return GetValue() == rhs.GetValue();
+ }
+
+ // Return the hash code stored in the lock word, must be kHashCode state.
+ int32_t GetHashCode() const;
+
+ uint32_t GetValue() const {
+ return value_;
+ }
+
+ private:
+ explicit LockWord(uint32_t val) : value_(val) {}
+
+ // Only Object should be converting LockWords to/from uints.
+ friend class mirror::Object;
+
+ // The encoded value holding all the state.
+ uint32_t value_;
+};
+std::ostream& operator<<(std::ostream& os, const LockWord::LockState& code);
+
+} // namespace art
+
+
+#endif // ART_RUNTIME_LOCK_WORD_H_
diff --git a/runtime/locks.h b/runtime/locks.h
index f63e2b1720..226221822b 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -53,8 +53,8 @@ enum LockLevel {
kJdwpAttachLock,
kJdwpStartLock,
kRuntimeShutdownLock,
- kHeapBitmapLock,
kMonitorLock,
+ kHeapBitmapLock,
kMutatorLock,
kZygoteCreationLock,
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 7802acc88b..3afb6065b6 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -67,7 +67,8 @@ static void CheckMapRequest(byte* addr, size_t byte_count) {
static void CheckMapRequest(byte*, size_t) { }
#endif
-MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot) {
+MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot,
+ std::string* error_msg) {
if (byte_count == 0) {
return new MemMap(name, NULL, 0, NULL, 0, prot);
}
@@ -82,8 +83,8 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, in
ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), page_aligned_byte_count));
int flags = MAP_PRIVATE;
if (fd.get() == -1) {
- PLOG(ERROR) << "ashmem_create_region failed (" << name << ")";
- return NULL;
+ *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s", name, strerror(errno));
+ return nullptr;
}
#else
ScopedFd fd(-1);
@@ -94,16 +95,17 @@ MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, in
if (actual == MAP_FAILED) {
std::string maps;
ReadFileToString("/proc/self/maps", &maps);
- PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_byte_count
- << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name
- << "\n" << maps;
- return NULL;
+ *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s",
+ addr, page_aligned_byte_count, prot, flags, fd.get(),
+ maps.c_str());
+ return nullptr;
}
return new MemMap(name, actual, byte_count, actual, page_aligned_byte_count, prot);
}
-MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count,
- int prot, int flags, int fd, off_t start, bool reuse) {
+MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd,
+ off_t start, bool reuse, const char* filename,
+ std::string* error_msg) {
CHECK_NE(0, prot);
CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
if (byte_count == 0) {
@@ -133,10 +135,10 @@ MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count,
if (actual == MAP_FAILED) {
std::string maps;
ReadFileToString("/proc/self/maps", &maps);
- PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(page_aligned_addr)
- << ", " << page_aligned_byte_count
- << ", " << prot << ", " << flags << ", " << fd << ", " << page_aligned_offset
- << ") failed\n" << maps;
+ *error_msg = StringPrintf("mmap(%p, %zd, %x, %x, %d, %lld) of file '%s' failed\n%s",
+ page_aligned_addr, page_aligned_byte_count, prot, flags, fd,
+ static_cast<int64_t>(page_aligned_offset),
+ filename, maps.c_str());
return NULL;
}
return new MemMap("file", actual + page_offset, byte_count, actual, page_aligned_byte_count,
@@ -168,12 +170,73 @@ MemMap::MemMap(const std::string& name, byte* begin, size_t size, void* base_beg
}
};
-void MemMap::UnMapAtEnd(byte* new_end) {
+MemMap* MemMap::RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
+ std::string* error_msg) {
DCHECK_GE(new_end, Begin());
DCHECK_LE(new_end, End());
- size_t unmap_size = End() - new_end;
- munmap(new_end, unmap_size);
- size_ -= unmap_size;
+ DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
+ DCHECK(IsAligned<kPageSize>(begin_));
+ DCHECK(IsAligned<kPageSize>(base_begin_));
+ DCHECK(IsAligned<kPageSize>(reinterpret_cast<byte*>(base_begin_) + base_size_));
+ DCHECK(IsAligned<kPageSize>(new_end));
+ byte* old_end = begin_ + size_;
+ byte* old_base_end = reinterpret_cast<byte*>(base_begin_) + base_size_;
+ byte* new_base_end = new_end;
+ DCHECK_LE(new_base_end, old_base_end);
+ if (new_base_end == old_base_end) {
+ return new MemMap(tail_name, NULL, 0, NULL, 0, tail_prot);
+ }
+ size_ = new_end - reinterpret_cast<byte*>(begin_);
+ base_size_ = new_base_end - reinterpret_cast<byte*>(base_begin_);
+ DCHECK_LE(begin_ + size_, reinterpret_cast<byte*>(base_begin_) + base_size_);
+ size_t tail_size = old_end - new_end;
+ byte* tail_base_begin = new_base_end;
+ size_t tail_base_size = old_base_end - new_base_end;
+ DCHECK_EQ(tail_base_begin + tail_base_size, old_base_end);
+ DCHECK(IsAligned<kPageSize>(tail_base_size));
+
+#ifdef USE_ASHMEM
+ // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
+ // prefixed "dalvik-".
+ std::string debug_friendly_name("dalvik-");
+ debug_friendly_name += tail_name;
+ ScopedFd fd(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
+ int flags = MAP_PRIVATE;
+ if (fd.get() == -1) {
+ *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
+ tail_name, strerror(errno));
+ return nullptr;
+ }
+#else
+ ScopedFd fd(-1);
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#endif
+
+ // Unmap/map the tail region.
+ int result = munmap(tail_base_begin, tail_base_size);
+ if (result == -1) {
+ std::string maps;
+ ReadFileToString("/proc/self/maps", &maps);
+ *error_msg = StringPrintf("munmap(%p, %zd) failed for '%s'\n%s",
+ tail_base_begin, tail_base_size, name_.c_str(),
+ maps.c_str());
+ return nullptr;
+ }
+ // Don't cause memory allocation between the munmap and the mmap
+ // calls. Otherwise, libc (or something else) might take this memory
+ // region. Note this isn't perfect as there's no way to prevent
+ // other threads to try to take this memory region here.
+ byte* actual = reinterpret_cast<byte*>(mmap(tail_base_begin, tail_base_size, tail_prot,
+ flags, fd.get(), 0));
+ if (actual == MAP_FAILED) {
+ std::string maps;
+ ReadFileToString("/proc/self/maps", &maps);
+ *error_msg = StringPrintf("anonymous mmap(%p, %zd, %x, %x, %d, 0) failed\n%s",
+ tail_base_begin, tail_base_size, tail_prot, flags, fd.get(),
+ maps.c_str());
+ return nullptr;
+ }
+ return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot);
}
bool MemMap::Protect(int prot) {
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index e294824d91..2c658334a5 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -38,14 +38,16 @@ class MemMap {
// a name.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot);
+ static MemMap* MapAnonymous(const char* ashmem_name, byte* addr, size_t byte_count, int prot,
+ std::string* error_msg);
// Map part of a file, taking care of non-page aligned offsets. The
// "start" offset is absolute, not relative.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start) {
- return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false);
+ static MemMap* MapFile(size_t byte_count, int prot, int flags, int fd, off_t start,
+ const char* filename, std::string* error_msg) {
+ return MapFileAtAddress(NULL, byte_count, prot, flags, fd, start, false, filename, error_msg);
}
// Map part of a file, taking care of non-page aligned offsets. The
@@ -53,8 +55,9 @@ class MemMap {
// requesting a specific address for the base of the mapping.
//
// On success, returns returns a MemMap instance. On failure, returns a NULL;
- static MemMap* MapFileAtAddress(
- byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start, bool reuse);
+ static MemMap* MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd,
+ off_t start, bool reuse, const char* filename,
+ std::string* error_msg);
// Releases the memory mapping
~MemMap();
@@ -81,8 +84,9 @@ class MemMap {
return Begin() <= addr && addr < End();
}
- // Trim by unmapping pages at the end of the map.
- void UnMapAtEnd(byte* new_end);
+ // Unmap the pages at end and remap them to create another memory map.
+ MemMap* RemapAtEnd(byte* new_end, const char* tail_name, int tail_prot,
+ std::string* error_msg);
private:
MemMap(const std::string& name, byte* begin, size_t size, void* base_begin, size_t base_size,
@@ -93,8 +97,10 @@ class MemMap {
size_t size_; // Length of data.
void* const base_begin_; // Page-aligned base address.
- const size_t base_size_; // Length of mapping.
+ size_t base_size_; // Length of mapping. May be changed by RemapAtEnd (ie Zygote).
int prot_; // Protection of the map.
+
+ friend class MemMapTest; // To allow access to base_begin_ and base_size_.
};
} // namespace art
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index dade01b23f..cf2c9d028c 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -21,14 +21,78 @@
namespace art {
-class MemMapTest : public testing::Test {};
+class MemMapTest : public testing::Test {
+ public:
+ byte* BaseBegin(MemMap* mem_map) {
+ return reinterpret_cast<byte*>(mem_map->base_begin_);
+ }
+ size_t BaseSize(MemMap* mem_map) {
+ return mem_map->base_size_;
+ }
+};
TEST_F(MemMapTest, MapAnonymousEmpty) {
+ std::string error_msg;
UniquePtr<MemMap> map(MemMap::MapAnonymous("MapAnonymousEmpty",
NULL,
0,
- PROT_READ));
- ASSERT_TRUE(map.get() != NULL);
+ PROT_READ,
+ &error_msg));
+ ASSERT_TRUE(map.get() != NULL) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
+}
+
+TEST_F(MemMapTest, RemapAtEnd) {
+ std::string error_msg;
+ // Cast the page size to size_t.
+ const size_t page_size = static_cast<size_t>(kPageSize);
+ // Map a two-page memory region.
+ MemMap* m0 = MemMap::MapAnonymous("MemMapTest_RemapAtEndTest_map0",
+ NULL,
+ 2 * page_size,
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ // Check its state and write to it.
+ byte* base0 = m0->Begin();
+ ASSERT_TRUE(base0 != NULL) << error_msg;
+ size_t size0 = m0->Size();
+ EXPECT_EQ(m0->Size(), 2 * page_size);
+ EXPECT_EQ(BaseBegin(m0), base0);
+ EXPECT_EQ(BaseSize(m0), size0);
+ memset(base0, 42, 2 * page_size);
+ // Remap the latter half into a second MemMap.
+ MemMap* m1 = m0->RemapAtEnd(base0 + page_size,
+ "MemMapTest_RemapAtEndTest_map1",
+ PROT_READ | PROT_WRITE,
+ &error_msg);
+ // Check the states of the two maps.
+ EXPECT_EQ(m0->Begin(), base0) << error_msg;
+ EXPECT_EQ(m0->Size(), page_size);
+ EXPECT_EQ(BaseBegin(m0), base0);
+ EXPECT_EQ(BaseSize(m0), page_size);
+ byte* base1 = m1->Begin();
+ size_t size1 = m1->Size();
+ EXPECT_EQ(base1, base0 + page_size);
+ EXPECT_EQ(size1, page_size);
+ EXPECT_EQ(BaseBegin(m1), base1);
+ EXPECT_EQ(BaseSize(m1), size1);
+ // Write to the second region.
+ memset(base1, 43, page_size);
+ // Check the contents of the two regions.
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(base0[i], 42);
+ }
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(base1[i], 43);
+ }
+ // Unmap the first region.
+ delete m0;
+ // Make sure the second region is still accessible after the first
+ // region is unmapped.
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(base1[i], 43);
+ }
+ delete m1;
}
} // namespace art
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index eb73c7dd38..c60e714d44 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -20,6 +20,9 @@
#include "array.h"
#include "class.h"
+#include "gc/heap-inl.h"
+#include "thread.h"
+#include "utils.h"
namespace art {
namespace mirror {
@@ -33,6 +36,68 @@ inline size_t Array::SizeOf() const {
return header_size + data_size;
}
+static inline size_t ComputeArraySize(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DCHECK(array_class != NULL);
+ DCHECK_GE(component_count, 0);
+ DCHECK(array_class->IsArrayClass());
+
+ size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
+ size_t data_size = component_count * component_size;
+ size_t size = header_size + data_size;
+
+ // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
+ size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size);
+ if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) {
+ self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
+ PrettyDescriptor(array_class).c_str(),
+ component_count).c_str());
+ return 0; // failure
+ }
+ return size;
+}
+
+static inline Array* SetArrayLength(Array* array, size_t length) {
+ if (LIKELY(array != NULL)) {
+ DCHECK(array->IsArrayInstance());
+ array->SetLength(length);
+ }
+ return array;
+}
+
+inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size) {
+ size_t size = ComputeArraySize(self, array_class, component_count, component_size);
+ if (UNLIKELY(size == 0)) {
+ return NULL;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ Array* array = down_cast<Array*>(heap->AllocObjectInstrumented(self, array_class, size));
+ return SetArrayLength(array, component_count);
+}
+
+inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size) {
+ size_t size = ComputeArraySize(self, array_class, component_count, component_size);
+ if (UNLIKELY(size == 0)) {
+ return NULL;
+ }
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ Array* array = down_cast<Array*>(heap->AllocObjectUninstrumented(self, array_class, size));
+ return SetArrayLength(array, component_count);
+}
+
+inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) {
+ DCHECK(array_class->IsArrayClass());
+ return AllocInstrumented(self, array_class, component_count, array_class->GetComponentSize());
+}
+
+inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) {
+ DCHECK(array_class->IsArrayClass());
+ return AllocUninstrumented(self, array_class, component_count, array_class->GetComponentSize());
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 88cd309eeb..020085dbf0 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -32,39 +32,6 @@
namespace art {
namespace mirror {
-Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count,
- size_t component_size) {
- DCHECK(array_class != NULL);
- DCHECK_GE(component_count, 0);
- DCHECK(array_class->IsArrayClass());
-
- size_t header_size = sizeof(Object) + (component_size == sizeof(int64_t) ? 8 : 4);
- size_t data_size = component_count * component_size;
- size_t size = header_size + data_size;
-
- // Check for overflow and throw OutOfMemoryError if this was an unreasonable request.
- size_t component_shift = sizeof(size_t) * 8 - 1 - CLZ(component_size);
- if (UNLIKELY(data_size >> component_shift != size_t(component_count) || size < data_size)) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- PrettyDescriptor(array_class).c_str(),
- component_count).c_str());
- return NULL;
- }
-
- gc::Heap* heap = Runtime::Current()->GetHeap();
- Array* array = down_cast<Array*>(heap->AllocObject(self, array_class, size));
- if (array != NULL) {
- DCHECK(array->IsArrayInstance());
- array->SetLength(component_count);
- }
- return array;
-}
-
-Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) {
- DCHECK(array_class->IsArrayClass());
- return Alloc(self, array_class, component_count, array_class->GetComponentSize());
-}
-
// Create a multi-dimensional array of Objects or primitive types.
//
// We have to generate the names for X[], X[][], X[][][], and so on. The
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index db6132df59..570dcaa292 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -27,10 +27,24 @@ class MANAGED Array : public Object {
// A convenience for code that doesn't know the component size,
// and doesn't want to have to work it out itself.
static Array* Alloc(Thread* self, Class* array_class, int32_t component_count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocInstrumented(self, array_class, component_count);
+ }
+ static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Array* Alloc(Thread* self, Class* array_class, int32_t component_count,
size_t component_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocInstrumented(self, array_class, component_count, component_size);
+ }
+ static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count,
+ size_t component_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions)
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index ccf3e59f18..c9bf1609a0 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -202,6 +202,13 @@ inline bool ArtMethod::IsResolutionMethod() const {
DCHECK(!result || IsRuntimeMethod());
return result;
}
+
+inline bool ArtMethod::IsImtConflictMethod() const {
+ bool result = this == Runtime::Current()->GetImtConflictMethod();
+ // Check that if we do think it is phony it looks like the imt conflict method.
+ DCHECK(!result || IsRuntimeMethod());
+ return result;
+}
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index cd05f41cc2..f5c0e9f216 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -306,11 +306,15 @@ bool ArtMethod::IsRegistered() const {
}
extern "C" void art_work_around_app_jni_bugs(JNIEnv*, jobject);
-void ArtMethod::RegisterNative(Thread* self, const void* native_method) {
+void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) {
DCHECK(Thread::Current() == self);
CHECK(IsNative()) << PrettyMethod(this);
+ CHECK(!IsFastNative()) << PrettyMethod(this);
CHECK(native_method != NULL) << PrettyMethod(this);
if (!self->GetJniEnv()->vm->work_around_app_jni_bugs) {
+ if (is_fast) {
+ SetAccessFlags(GetAccessFlags() | kAccFastNative);
+ }
SetNativeMethod(native_method);
} else {
// We've been asked to associate this method with the given native method but are working
@@ -328,9 +332,9 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method) {
}
void ArtMethod::UnregisterNative(Thread* self) {
- CHECK(IsNative()) << PrettyMethod(this);
+ CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
// restore stub to lookup native pointer via dlsym
- RegisterNative(self, GetJniDlsymLookupStub());
+ RegisterNative(self, GetJniDlsymLookupStub(), false);
}
void ArtMethod::SetNativeMethod(const void* native_method) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 5d4a6ea0d5..f396fbe436 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -112,6 +112,10 @@ class MANAGED ArtMethod : public Object {
return (GetAccessFlags() & kAccNative) != 0;
}
+ bool IsFastNative() const {
+ return (GetAccessFlags() & kAccFastNative) != 0;
+ }
+
bool IsAbstract() const {
return (GetAccessFlags() & kAccAbstract) != 0;
}
@@ -307,7 +311,7 @@ class MANAGED ArtMethod : public Object {
bool IsRegistered() const;
- void RegisterNative(Thread* self, const void* native_method)
+ void RegisterNative(Thread* self, const void* native_method, bool is_fast)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -353,6 +357,8 @@ class MANAGED ArtMethod : public Object {
bool IsResolutionMethod() const;
+ bool IsImtConflictMethod() const;
+
uintptr_t NativePcOffset(const uintptr_t pc) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Converts a native PC to a dex PC.
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 1e1138745d..7f3a302768 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -23,6 +23,7 @@
#include "art_method.h"
#include "class_loader.h"
#include "dex_cache.h"
+#include "gc/heap-inl.h"
#include "iftable.h"
#include "object_array-inl.h"
#include "runtime.h"
@@ -34,9 +35,7 @@ namespace mirror {
inline size_t Class::GetObjectSize() const {
DCHECK(!IsVariableSize()) << " class=" << PrettyTypeOf(this);
DCHECK_EQ(sizeof(size_t), sizeof(int32_t));
- size_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), false);
- DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(this);
- return result;
+ return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, object_size_), false);
}
inline Class* Class::GetSuperClass() const {
@@ -140,6 +139,14 @@ inline void Class::SetVTable(ObjectArray<ArtMethod>* new_vtable)
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, vtable_), new_vtable, false);
}
+inline ObjectArray<ArtMethod>* Class::GetImTable() const {
+ return GetFieldObject<ObjectArray<ArtMethod>*>(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), false);
+}
+
+inline void Class::SetImTable(ObjectArray<ArtMethod>* new_imtable) {
+ SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, imtable_), new_imtable, false);
+}
+
inline bool Class::Implements(const Class* klass) const {
DCHECK(klass != NULL);
DCHECK(klass->IsInterface()) << PrettyClass(this);
@@ -342,6 +349,24 @@ inline void Class::SetName(String* name) {
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, name_), name, false);
}
+inline void Class::CheckObjectAlloc() {
+ DCHECK(!IsArrayClass()) << PrettyClass(this);
+ DCHECK(IsInstantiable()) << PrettyClass(this);
+ // TODO: decide whether we want this check. It currently fails during bootstrap.
+ // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this);
+ DCHECK_GE(this->object_size_, sizeof(Object));
+}
+
+inline Object* Class::AllocObjectInstrumented(Thread* self) {
+ CheckObjectAlloc();
+ return Runtime::Current()->GetHeap()->AllocObjectInstrumented(self, this, this->object_size_);
+}
+
+inline Object* Class::AllocObjectUninstrumented(Thread* self) {
+ CheckObjectAlloc();
+ return Runtime::Current()->GetHeap()->AllocObjectUninstrumented(self, this, this->object_size_);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index add7e1b2af..f3cb54aa15 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -60,7 +60,7 @@ void Class::SetStatus(Status new_status, Thread* self) {
}
if (new_status >= kStatusResolved || old_status >= kStatusResolved) {
// When classes are being resolved the resolution code should hold the lock.
- CHECK_EQ(GetThinLockId(), self->GetThinLockId())
+ CHECK_EQ(GetLockOwnerThreadId(), self->GetThreadId())
<< "Attempt to change status of class while not holding its lock: "
<< PrettyClass(this) << " " << old_status << " -> " << new_status;
}
@@ -118,15 +118,6 @@ void Class::SetDexCache(DexCache* new_dex_cache) {
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache, false);
}
-Object* Class::AllocObject(Thread* self) {
- DCHECK(!IsArrayClass()) << PrettyClass(this);
- DCHECK(IsInstantiable()) << PrettyClass(this);
- // TODO: decide whether we want this check. It currently fails during bootstrap.
- // DCHECK(!Runtime::Current()->IsStarted() || IsInitializing()) << PrettyClass(this);
- DCHECK_GE(this->object_size_, sizeof(Object));
- return Runtime::Current()->GetHeap()->AllocObject(self, this, this->object_size_);
-}
-
void Class::SetClassSize(size_t new_class_size) {
if (kIsDebugBuild && (new_class_size < GetClassSize())) {
DumpClass(LOG(ERROR), kDumpClassFullDetail);
@@ -334,7 +325,7 @@ void Class::SetClassLoader(ClassLoader* new_class_loader) {
SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader, false);
}
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const StringPiece& signature) const {
+ArtMethod* Class::FindInterfaceMethod(const StringPiece& name, const Signature& signature) const {
// Check the current class before checking the interfaces.
ArtMethod* method = FindDeclaredVirtualMethod(name, signature);
if (method != NULL) {
@@ -370,12 +361,23 @@ ArtMethod* Class::FindInterfaceMethod(const DexCache* dex_cache, uint32_t dex_me
return NULL;
}
-
ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const {
MethodHelper mh;
for (size_t i = 0; i < NumDirectMethods(); ++i) {
ArtMethod* method = GetDirectMethod(i);
mh.ChangeMethod(method);
+ if (name == mh.GetName() && mh.GetSignature() == signature) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
+ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name, const Signature& signature) const {
+ MethodHelper mh;
+ for (size_t i = 0; i < NumDirectMethods(); ++i) {
+ ArtMethod* method = GetDirectMethod(i);
+ mh.ChangeMethod(method);
if (name == mh.GetName() && signature == mh.GetSignature()) {
return method;
}
@@ -405,6 +407,16 @@ ArtMethod* Class::FindDirectMethod(const StringPiece& name, const StringPiece& s
return NULL;
}
+ArtMethod* Class::FindDirectMethod(const StringPiece& name, const Signature& signature) const {
+ for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+ ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtMethod* Class::FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const {
for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
ArtMethod* method = klass->FindDeclaredDirectMethod(dex_cache, dex_method_idx);
@@ -415,8 +427,20 @@ ArtMethod* Class::FindDirectMethod(const DexCache* dex_cache, uint32_t dex_metho
return NULL;
}
+ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const {
+ MethodHelper mh;
+ for (size_t i = 0; i < NumVirtualMethods(); ++i) {
+ ArtMethod* method = GetVirtualMethod(i);
+ mh.ChangeMethod(method);
+ if (name == mh.GetName() && mh.GetSignature() == signature) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature) const {
+ const Signature& signature) const {
MethodHelper mh;
for (size_t i = 0; i < NumVirtualMethods(); ++i) {
ArtMethod* method = GetVirtualMethod(i);
@@ -450,6 +474,16 @@ ArtMethod* Class::FindVirtualMethod(const StringPiece& name, const StringPiece&
return NULL;
}
+ArtMethod* Class::FindVirtualMethod(const StringPiece& name, const Signature& signature) const {
+ for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
+ ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtMethod* Class::FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const {
for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) {
ArtMethod* method = klass->FindDeclaredVirtualMethod(dex_cache, dex_method_idx);
@@ -460,6 +494,22 @@ ArtMethod* Class::FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_meth
return NULL;
}
+ArtMethod* Class::FindClassInitializer() const {
+ for (size_t i = 0; i < NumDirectMethods(); ++i) {
+ ArtMethod* method = GetDirectMethod(i);
+ if (method->IsConstructor() && method->IsStatic()) {
+ if (kIsDebugBuild) {
+ MethodHelper mh(method);
+ CHECK(mh.IsClassInitializer());
+ CHECK_STREQ(mh.GetName(), "<clinit>");
+ CHECK_STREQ(mh.GetSignature().ToString().c_str(), "()V");
+ }
+ return method;
+ }
+ }
+ return NULL;
+}
+
ArtField* Class::FindDeclaredInstanceField(const StringPiece& name, const StringPiece& type) {
// Is the field in this class?
// Interfaces are not relevant because they can't contain instance fields.
@@ -611,7 +661,9 @@ static void SetPreverifiedFlagOnMethods(mirror::ObjectArray<mirror::ArtMethod>*
for (int32_t index = 0, end = methods->GetLength(); index < end; ++index) {
mirror::ArtMethod* method = methods->GetWithoutChecks(index);
DCHECK(method != NULL);
- method->SetPreverified();
+ if (!method->IsNative() && !method->IsAbstract()) {
+ method->SetPreverified();
+ }
}
}
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d97b603ad8..ed1aad39d2 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -59,6 +59,7 @@ namespace art {
struct ClassClassOffsets;
struct ClassOffsets;
+class Signature;
class StringPiece;
namespace mirror {
@@ -121,6 +122,7 @@ class MANAGED Class : public StaticStorageBase {
kStatusVerified = 7, // Logically part of linking; done pre-init.
kStatusInitializing = 8, // Class init in progress.
kStatusInitialized = 9, // Ready to go.
+ kStatusMax = 10,
};
Status GetStatus() const {
@@ -246,7 +248,7 @@ class MANAGED Class : public StaticStorageBase {
} else {
Class* component = GetComponentType();
if (component->IsPrimitive()) {
- return false;
+ return true;
} else {
return component->CannotBeAssignedFromOtherTypes();
}
@@ -345,14 +347,18 @@ class MANAGED Class : public StaticStorageBase {
bool IsArtMethodClass() const;
+ static MemberOffset ComponentTypeOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(Class, component_type_);
+ }
+
Class* GetComponentType() const {
- return GetFieldObject<Class*>(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), false);
+ return GetFieldObject<Class*>(ComponentTypeOffset(), false);
}
void SetComponentType(Class* new_component_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(GetComponentType() == NULL);
DCHECK(new_component_type != NULL);
- SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, component_type_), new_component_type, false);
+ SetFieldObject(ComponentTypeOffset(), new_component_type, false);
}
size_t GetComponentSize() const {
@@ -371,7 +377,12 @@ class MANAGED Class : public StaticStorageBase {
}
// Creates a raw object instance but does not invoke the default constructor.
- Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return AllocObjectInstrumented(self);
+ }
+
+ Object* AllocObjectUninstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ Object* AllocObjectInstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
bool IsVariableSize() const {
// Classes and arrays vary in size, and so the object_size_ field cannot
@@ -544,6 +555,15 @@ class MANAGED Class : public StaticStorageBase {
return OFFSET_OF_OBJECT_MEMBER(Class, vtable_);
}
+ ObjectArray<ArtMethod>* GetImTable() const;
+
+ void SetImTable(ObjectArray<ArtMethod>* new_imtable)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static MemberOffset ImTableOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(Class, imtable_);
+ }
+
// Given a method implemented by this class but potentially from a super class, return the
// specific implementation method for this class.
ArtMethod* FindVirtualMethodForVirtual(ArtMethod* method) const
@@ -560,39 +580,53 @@ class MANAGED Class : public StaticStorageBase {
ArtMethod* FindVirtualMethodForInterface(ArtMethod* method) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE;
- ArtMethod* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const
+ ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindInterfaceMethod(const StringPiece& name, const Signature& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
ArtMethod* FindInterfaceMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method) const
+ ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const
+ ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const Signature& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ ArtMethod* FindDeclaredDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& descriptor) const
+ ArtMethod* FindDirectMethod(const StringPiece& name, const StringPiece& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ ArtMethod* FindDirectMethod(const StringPiece& name, const Signature& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) const
+ ArtMethod* FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindDirectMethod(const StringPiece& name, const StringPiece& signature) const
+ ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name, const Signature& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- ArtMethod* FindDirectMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ ArtMethod* FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindVirtualMethod(const StringPiece& name, const StringPiece& signature) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindVirtualMethod(const StringPiece& name, const Signature& signature) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ArtMethod* FindVirtualMethod(const DexCache* dex_cache, uint32_t dex_method_idx) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* FindClassInitializer() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
int32_t GetIfTableCount() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
IfTable* GetIfTable() const;
@@ -764,6 +798,8 @@ class MANAGED Class : public StaticStorageBase {
bool IsAssignableFromArray(const Class* klass) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckObjectAlloc() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// defining class loader, or NULL for the "bootstrap" system loader
ClassLoader* class_loader_;
@@ -803,6 +839,9 @@ class MANAGED Class : public StaticStorageBase {
// methods for the methods in the interface.
IfTable* iftable_;
+ // Interface method table (imt), for quick "invoke-interface".
+ ObjectArray<ArtMethod>* imtable_;
+
// descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName
String* name_;
@@ -885,6 +924,7 @@ std::ostream& operator<<(std::ostream& os, const Class::Status& rhs);
class MANAGED ClassClass : public Class {
private:
+ int32_t pad_;
int64_t serialVersionUID_;
friend struct art::ClassClassOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassClass);
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 5ed3db342c..7ac2c8c083 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -24,6 +24,7 @@
#include "atomic.h"
#include "array-inl.h"
#include "class.h"
+#include "lock_word-inl.h"
#include "monitor.h"
#include "runtime.h"
#include "throwable.h"
@@ -43,8 +44,21 @@ inline void Object::SetClass(Class* new_klass) {
SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(Object, klass_), new_klass, false, false);
}
-inline uint32_t Object::GetThinLockId() {
- return Monitor::GetThinLockId(monitor_);
+inline LockWord Object::GetLockWord() const {
+ return LockWord(GetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), true));
+}
+
+inline void Object::SetLockWord(LockWord new_val) {
+ SetField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), new_val.GetValue(), true);
+}
+
+inline bool Object::CasLockWord(LockWord old_val, LockWord new_val) {
+ return CasField32(OFFSET_OF_OBJECT_MEMBER(Object, monitor_), old_val.GetValue(),
+ new_val.GetValue());
+}
+
+inline uint32_t Object::GetLockOwnerThreadId() {
+ return Monitor::GetLockOwnerThreadId(this);
}
inline void Object::MonitorEnter(Thread* self) {
@@ -233,11 +247,19 @@ inline size_t Object::SizeOf() const {
} else {
result = GetClass()->GetObjectSize();
}
+ DCHECK_GE(result, sizeof(Object)) << " class=" << PrettyTypeOf(GetClass());
DCHECK(!IsArtField() || result == sizeof(ArtField));
DCHECK(!IsArtMethod() || result == sizeof(ArtMethod));
return result;
}
+inline bool Object::CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value) {
+ VerifyObject(this);
+ byte* raw_addr = reinterpret_cast<byte*>(this) + field_offset.Int32Value();
+ int32_t* addr = reinterpret_cast<int32_t*>(raw_addr);
+ return android_atomic_release_cas(old_value, new_value, addr) == 0;
+}
+
inline uint64_t Object::GetField64(MemberOffset field_offset, bool is_volatile) const {
VerifyObject(this);
const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 92c05b26ca..bd187c1835 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <ctime>
+
#include "object.h"
#include "art_field.h"
@@ -82,6 +84,52 @@ Object* Object::Clone(Thread* self) {
return copy.get();
}
+int32_t Object::GenerateIdentityHashCode() {
+ static AtomicInteger seed(987654321 + std::time(nullptr));
+ int32_t expected_value, new_value;
+ do {
+ expected_value = static_cast<uint32_t>(seed.load());
+ new_value = expected_value * 1103515245 + 12345;
+ } while ((expected_value & LockWord::kHashMask) == 0 ||
+ !seed.compare_and_swap(expected_value, new_value));
+ return expected_value & LockWord::kHashMask;
+}
+
+int32_t Object::IdentityHashCode() const {
+ while (true) {
+ LockWord lw = GetLockWord();
+ switch (lw.GetState()) {
+ case LockWord::kUnlocked: {
+ // Try to compare and swap in a new hash, if we succeed we will return the hash on the next
+ // loop iteration.
+ LockWord hash_word(LockWord::FromHashCode(GenerateIdentityHashCode()));
+ DCHECK_EQ(hash_word.GetState(), LockWord::kHashCode);
+ if (const_cast<Object*>(this)->CasLockWord(lw, hash_word)) {
+ return hash_word.GetHashCode();
+ }
+ break;
+ }
+ case LockWord::kThinLocked: {
+ // Inflate the thin lock to a monitor and stick the hash code inside of the monitor.
+ Thread* self = Thread::Current();
+ Monitor::InflateThinLocked(self, const_cast<Object*>(this), lw, GenerateIdentityHashCode());
+ break;
+ }
+ case LockWord::kFatLocked: {
+ // Already inflated, return the has stored in the monitor.
+ Monitor* monitor = lw.FatLockMonitor();
+ DCHECK(monitor != nullptr);
+ return monitor->GetHashCode();
+ }
+ case LockWord::kHashCode: {
+ return lw.GetHashCode();
+ }
+ }
+ }
+ LOG(FATAL) << "Unreachable";
+ return 0;
+}
+
void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, const Object* new_value) {
const Class* c = GetClass();
if (Runtime::Current()->GetClassLinker() == NULL ||
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index e105525f79..e8ea3f2375 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -26,6 +26,8 @@
namespace art {
class ImageWriter;
+class LockWord;
+class Monitor;
struct ObjectOffsets;
class Thread;
@@ -83,26 +85,16 @@ class MANAGED Object {
Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- int32_t IdentityHashCode() const {
-#ifdef MOVING_GARBAGE_COLLECTOR
- // TODO: we'll need to use the Object's internal concept of identity
- UNIMPLEMENTED(FATAL);
-#endif
- return reinterpret_cast<int32_t>(this);
- }
+ int32_t IdentityHashCode() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static MemberOffset MonitorOffset() {
return OFFSET_OF_OBJECT_MEMBER(Object, monitor_);
}
- volatile int32_t* GetRawLockWordAddress() {
- byte* raw_addr = reinterpret_cast<byte*>(this) +
- OFFSET_OF_OBJECT_MEMBER(Object, monitor_).Int32Value();
- int32_t* word_addr = reinterpret_cast<int32_t*>(raw_addr);
- return const_cast<volatile int32_t*>(word_addr);
- }
-
- uint32_t GetThinLockId();
+ LockWord GetLockWord() const;
+ void SetLockWord(LockWord new_val);
+ bool CasLockWord(LockWord old_val, LockWord new_val);
+ uint32_t GetLockOwnerThreadId();
void MonitorEnter(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCK_FUNCTION(monitor_lock_);
@@ -189,6 +181,11 @@ class MANAGED Object {
}
}
+ Object** GetFieldObjectAddr(MemberOffset field_offset) ALWAYS_INLINE {
+ VerifyObject(this);
+ return reinterpret_cast<Object**>(reinterpret_cast<byte*>(this) + field_offset.Int32Value());
+ }
+
uint32_t GetField32(MemberOffset field_offset, bool is_volatile) const {
VerifyObject(this);
const byte* raw_addr = reinterpret_cast<const byte*>(this) + field_offset.Int32Value();
@@ -221,6 +218,8 @@ class MANAGED Object {
}
}
+ bool CasField32(MemberOffset field_offset, uint32_t old_value, uint32_t new_value);
+
uint64_t GetField64(MemberOffset field_offset, bool is_volatile) const;
void SetField64(MemberOffset field_offset, uint64_t new_value, bool is_volatile);
@@ -239,7 +238,6 @@ class MANAGED Object {
private:
static void VerifyObject(const Object* obj) ALWAYS_INLINE;
-
// Verify the type correctness of stores to fields.
void CheckFieldAssignmentImpl(MemberOffset field_offset, const Object* new_value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -250,6 +248,9 @@ class MANAGED Object {
}
}
+ // Generate an identity hash code.
+ static int32_t GenerateIdentityHashCode();
+
// Write barrier called post update to a reference bearing field.
static void WriteBarrierField(const Object* dst, MemberOffset offset, const Object* new_value);
@@ -258,6 +259,7 @@ class MANAGED Object {
uint32_t monitor_;
friend class art::ImageWriter;
+ friend class art::Monitor;
friend struct art::ObjectOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
};
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index b8765afadc..d0d1ee4a46 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -71,12 +71,21 @@ class ObjectTest : public CommonTest {
// Keep the assembly code in sync
TEST_F(ObjectTest, AsmConstants) {
- ASSERT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value());
- ASSERT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value());
- ASSERT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value());
- ASSERT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
+ EXPECT_EQ(CLASS_OFFSET, Object::ClassOffset().Int32Value());
+ EXPECT_EQ(LOCK_WORD_OFFSET, Object::MonitorOffset().Int32Value());
- ASSERT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value());
+ EXPECT_EQ(CLASS_COMPONENT_TYPE_OFFSET, Class::ComponentTypeOffset().Int32Value());
+
+ EXPECT_EQ(ARRAY_LENGTH_OFFSET, Array::LengthOffset().Int32Value());
+ EXPECT_EQ(OBJECT_ARRAY_DATA_OFFSET, Array::DataOffset(sizeof(Object*)).Int32Value());
+
+ EXPECT_EQ(STRING_VALUE_OFFSET, String::ValueOffset().Int32Value());
+ EXPECT_EQ(STRING_COUNT_OFFSET, String::CountOffset().Int32Value());
+ EXPECT_EQ(STRING_OFFSET_OFFSET, String::OffsetOffset().Int32Value());
+ EXPECT_EQ(STRING_DATA_OFFSET, Array::DataOffset(sizeof(uint16_t)).Int32Value());
+
+ EXPECT_EQ(METHOD_DEX_CACHE_METHODS_OFFSET, ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ EXPECT_EQ(METHOD_CODE_OFFSET, ArtMethod::EntryPointFromCompiledCodeOffset().Int32Value());
}
TEST_F(ObjectTest, IsInSamePackage) {
@@ -262,7 +271,7 @@ TEST_F(ObjectTest, StaticFieldFromCode) {
Class* klass =
class_linker_->FindClass("LStaticsFromCode;", soa.Decode<ClassLoader*>(class_loader));
- ArtMethod* clinit = klass->FindDirectMethod("<clinit>", "()V");
+ ArtMethod* clinit = klass->FindClassInitializer();
const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;");
ASSERT_TRUE(klass_string_id != NULL);
const DexFile::TypeId* klass_type_id = dex_file->FindTypeId(
@@ -283,8 +292,8 @@ TEST_F(ObjectTest, StaticFieldFromCode) {
ASSERT_TRUE(field_id != NULL);
uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id);
- ArtField* field = FindFieldFromCode(field_idx, clinit, Thread::Current(), StaticObjectRead,
- sizeof(Object*), true);
+ ArtField* field = FindFieldFromCode<StaticObjectRead, true>(field_idx, clinit, Thread::Current(),
+ sizeof(Object*));
Object* s0 = field->GetObj(klass);
EXPECT_TRUE(s0 != NULL);
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index a505ed00a3..9d76c6bc11 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -17,6 +17,7 @@
#include "stack_trace_element.h"
#include "class.h"
+#include "class-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "object-inl.h"
#include "string.h"
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index f8a0e531eb..9c93f17f8e 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -17,13 +17,14 @@
#include "string.h"
#include "array.h"
+#include "class-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "intern_table.h"
#include "object-inl.h"
#include "runtime.h"
#include "sirt_ref.h"
#include "thread.h"
-#include "utf.h"
+#include "utf-inl.h"
namespace art {
namespace mirror {
@@ -32,6 +33,10 @@ const CharArray* String::GetCharArray() const {
return GetFieldObject<const CharArray*>(ValueOffset(), false);
}
+CharArray* String::GetCharArray() {
+ return GetFieldObject<CharArray*>(ValueOffset(), false);
+}
+
void String::ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()));
}
@@ -285,4 +290,3 @@ int32_t String::CompareTo(String* rhs) const {
} // namespace mirror
} // namespace art
-
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 81fe42f2a6..01d8f318ff 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -44,6 +44,7 @@ class MANAGED String : public Object {
}
const CharArray* GetCharArray() const;
+ CharArray* GetCharArray();
int32_t GetOffset() const {
int32_t result = GetField32(OffsetOffset(), false);
@@ -155,8 +156,8 @@ class MANAGED StringClass : public Class {
private:
CharArray* ASCII_;
Object* CASE_INSENSITIVE_ORDER_;
- int64_t serialVersionUID_;
uint32_t REPLACEMENT_CHAR_;
+ int64_t serialVersionUID_;
friend struct art::StringClassOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(StringClass);
};
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 34f4af845b..4e365be8e0 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -46,6 +46,7 @@ static const uint32_t kAccConstructor = 0x00010000; // method (dex only) <init>
static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (dex only)
static const uint32_t kAccClassIsProxy = 0x00040000; // class (dex only)
static const uint32_t kAccPreverified = 0x00080000; // method (dex only)
+static const uint32_t kAccFastNative = 0x0080000; // method (dex only)
// Special runtime-only flags.
// Note: if only kAccClassIsReference is set, we have a soft reference.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 088d1f7a88..2abfd3df41 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -23,6 +23,7 @@
#include "class_linker.h"
#include "dex_file-inl.h"
#include "dex_instruction.h"
+#include "lock_word-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
@@ -37,36 +38,20 @@
namespace art {
/*
- * Every Object has a monitor associated with it, but not every Object is
- * actually locked. Even the ones that are locked do not need a
- * full-fledged monitor until a) there is actual contention or b) wait()
- * is called on the Object.
+ * Every Object has a monitor associated with it, but not every Object is actually locked. Even
+ * the ones that are locked do not need a full-fledged monitor until a) there is actual contention
+ * or b) wait() is called on the Object.
*
- * For Android, we have implemented a scheme similar to the one described
- * in Bacon et al.'s "Thin locks: featherweight synchronization for Java"
- * (ACM 1998). Things are even easier for us, though, because we have
- * a full 32 bits to work with.
+ * For Android, we have implemented a scheme similar to the one described in Bacon et al.'s
+ * "Thin locks: featherweight synchronization for Java" (ACM 1998). Things are even easier for us,
+ * though, because we have a full 32 bits to work with.
*
- * The two states of an Object's lock are referred to as "thin" and
- * "fat". A lock may transition from the "thin" state to the "fat"
- * state and this transition is referred to as inflation. Once a lock
- * has been inflated it remains in the "fat" state indefinitely.
+ * The two states of an Object's lock are referred to as "thin" and "fat". A lock may transition
+ * from the "thin" state to the "fat" state and this transition is referred to as inflation. Once
+ * a lock has been inflated it remains in the "fat" state indefinitely.
*
- * The lock value itself is stored in Object.lock. The LSB of the
- * lock encodes its state. When cleared, the lock is in the "thin"
- * state and its bits are formatted as follows:
- *
- * [31 ---- 19] [18 ---- 3] [2 ---- 1] [0]
- * lock count thread id hash state 0
- *
- * When set, the lock is in the "fat" state and its bits are formatted
- * as follows:
- *
- * [31 ---- 3] [2 ---- 1] [0]
- * pointer hash state 1
- *
- * For an in-depth description of the mechanics of thin-vs-fat locking,
- * read the paper referred to above.
+ * The lock value itself is stored in mirror::Object::monitor_ and the representation is described
+ * in the LockWord value type.
*
* Monitors provide:
* - mutually exclusive access to resources
@@ -74,31 +59,10 @@ namespace art {
*
* In effect, they fill the role of both mutexes and condition variables.
*
- * Only one thread can own the monitor at any time. There may be several
- * threads waiting on it (the wait call unlocks it). One or more waiting
- * threads may be getting interrupted or notified at any given time.
- *
- * TODO: the various members of monitor are not SMP-safe.
- */
-
-// The shape is the bottom bit; either LW_SHAPE_THIN or LW_SHAPE_FAT.
-#define LW_SHAPE_MASK 0x1
-#define LW_SHAPE(x) static_cast<int>((x) & LW_SHAPE_MASK)
-
-/*
- * Monitor accessor. Extracts a monitor structure pointer from a fat
- * lock. Performs no error checking.
+ * Only one thread can own the monitor at any time. There may be several threads waiting on it
+ * (the wait call unlocks it). One or more waiting threads may be getting interrupted or notified
+ * at any given time.
*/
-#define LW_MONITOR(x) \
- (reinterpret_cast<Monitor*>((x) & ~((LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT) | LW_SHAPE_MASK)))
-
-/*
- * Lock recursion count field. Contains a count of the number of times
- * a lock has been recursively acquired.
- */
-#define LW_LOCK_COUNT_MASK 0x1fff
-#define LW_LOCK_COUNT_SHIFT 19
-#define LW_LOCK_COUNT(x) (((x) >> LW_LOCK_COUNT_SHIFT) & LW_LOCK_COUNT_MASK)
bool (*Monitor::is_sensitive_thread_hook_)() = NULL;
uint32_t Monitor::lock_profiling_threshold_ = 0;
@@ -115,31 +79,69 @@ void Monitor::Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread
is_sensitive_thread_hook_ = is_sensitive_thread_hook;
}
-Monitor::Monitor(Thread* owner, mirror::Object* obj)
+Monitor::Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code)
: monitor_lock_("a monitor lock", kMonitorLock),
+ monitor_contenders_("monitor contenders", monitor_lock_),
owner_(owner),
lock_count_(0),
obj_(obj),
wait_set_(NULL),
+ hash_code_(hash_code),
locking_method_(NULL),
locking_dex_pc_(0) {
- monitor_lock_.Lock(owner);
+ // We should only inflate a lock if the owner is ourselves or suspended. This avoids a race
+ // with the owner unlocking the thin-lock.
+ CHECK(owner == nullptr || owner == Thread::Current() || owner->IsSuspended());
+ // The identity hash code is set for the life time of the monitor.
+}
+
+int32_t Monitor::GetHashCode() {
+ while (!HasHashCode()) {
+ if (hash_code_.compare_and_swap(0, mirror::Object::GenerateIdentityHashCode())) {
+ break;
+ }
+ }
+ DCHECK(HasHashCode());
+ return hash_code_.load();
+}
+
+bool Monitor::Install(Thread* self) {
+ MutexLock mu(self, monitor_lock_); // Uncontended mutex acquisition as monitor isn't yet public.
+ CHECK(owner_ == nullptr || owner_ == self || owner_->IsSuspended());
// Propagate the lock state.
- uint32_t thin = *obj->GetRawLockWordAddress();
- lock_count_ = LW_LOCK_COUNT(thin);
- thin &= LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT;
- thin |= reinterpret_cast<uint32_t>(this) | LW_SHAPE_FAT;
- // Publish the updated lock word.
- android_atomic_release_store(thin, obj->GetRawLockWordAddress());
+ LockWord lw(obj_->GetLockWord());
+ switch (lw.GetState()) {
+ case LockWord::kThinLocked: {
+ CHECK_EQ(owner_->GetThreadId(), lw.ThinLockOwner());
+ lock_count_ = lw.ThinLockCount();
+ break;
+ }
+ case LockWord::kHashCode: {
+ CHECK_EQ(hash_code_, static_cast<int32_t>(lw.GetHashCode()));
+ break;
+ }
+ case LockWord::kFatLocked: {
+ // The owner_ is suspended but another thread beat us to install a monitor.
+ return false;
+ }
+ case LockWord::kUnlocked: {
+ LOG(FATAL) << "Inflating unlocked lock word";
+ break;
+ }
+ }
+ LockWord fat(this);
+ // Publish the updated lock word, which may race with other threads.
+ bool success = obj_->CasLockWord(lw, fat);
// Lock profiling.
- if (lock_profiling_threshold_ != 0) {
- locking_method_ = owner->GetCurrentMethod(&locking_dex_pc_);
+ if (success && owner_ != nullptr && lock_profiling_threshold_ != 0) {
+ locking_method_ = owner_->GetCurrentMethod(&locking_dex_pc_);
}
+ return success;
}
Monitor::~Monitor() {
- DCHECK(obj_ != NULL);
- DCHECK_EQ(LW_SHAPE(*obj_->GetRawLockWordAddress()), LW_SHAPE_FAT);
+ CHECK(obj_ != NULL);
+ CHECK_EQ(obj_->GetLockWord().GetState(), LockWord::kFatLocked);
}
/*
@@ -190,60 +192,56 @@ void Monitor::RemoveFromWaitSet(Thread *thread) {
}
}
-mirror::Object* Monitor::GetObject() {
- return obj_;
+void Monitor::SetObject(mirror::Object* object) {
+ obj_ = object;
}
void Monitor::Lock(Thread* self) {
- if (owner_ == self) {
- lock_count_++;
- return;
- }
-
- if (!monitor_lock_.TryLock(self)) {
- uint64_t waitStart = 0;
- uint64_t waitEnd = 0;
- uint32_t wait_threshold = lock_profiling_threshold_;
- const mirror::ArtMethod* current_locking_method = NULL;
- uint32_t current_locking_dex_pc = 0;
- {
- ScopedThreadStateChange tsc(self, kBlocked);
- if (wait_threshold != 0) {
- waitStart = NanoTime() / 1000;
- }
- current_locking_method = locking_method_;
- current_locking_dex_pc = locking_dex_pc_;
-
- monitor_lock_.Lock(self);
- if (wait_threshold != 0) {
- waitEnd = NanoTime() / 1000;
+ MutexLock mu(self, monitor_lock_);
+ while (true) {
+ if (owner_ == NULL) { // Unowned.
+ owner_ = self;
+ CHECK_EQ(lock_count_, 0);
+ // When debugging, save the current monitor holder for future
+ // acquisition failures to use in sampled logging.
+ if (lock_profiling_threshold_ != 0) {
+ locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
}
+ return;
+ } else if (owner_ == self) { // Recursive.
+ lock_count_++;
+ return;
}
-
- if (wait_threshold != 0) {
- uint64_t wait_ms = (waitEnd - waitStart) / 1000;
- uint32_t sample_percent;
- if (wait_ms >= wait_threshold) {
- sample_percent = 100;
- } else {
- sample_percent = 100 * wait_ms / wait_threshold;
- }
- if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
- const char* current_locking_filename;
- uint32_t current_locking_line_number;
- TranslateLocation(current_locking_method, current_locking_dex_pc,
- current_locking_filename, current_locking_line_number);
- LogContentionEvent(self, wait_ms, sample_percent, current_locking_filename, current_locking_line_number);
+ // Contended.
+ const bool log_contention = (lock_profiling_threshold_ != 0);
+ uint64_t wait_start_ms = log_contention ? 0 : MilliTime();
+ const mirror::ArtMethod* owners_method = locking_method_;
+ uint32_t owners_dex_pc = locking_dex_pc_;
+ monitor_lock_.Unlock(self); // Let go of locks in order.
+ {
+ ScopedThreadStateChange tsc(self, kBlocked); // Change to blocked and give up mutator_lock_.
+ MutexLock mu2(self, monitor_lock_); // Reacquire monitor_lock_ without mutator_lock_ for Wait.
+ if (owner_ != NULL) { // Did the owner_ give the lock up?
+ monitor_contenders_.Wait(self); // Still contended so wait.
+ // Woken from contention.
+ if (log_contention) {
+ uint64_t wait_ms = MilliTime() - wait_start_ms;
+ uint32_t sample_percent;
+ if (wait_ms >= lock_profiling_threshold_) {
+ sample_percent = 100;
+ } else {
+ sample_percent = 100 * wait_ms / lock_profiling_threshold_;
+ }
+ if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
+ const char* owners_filename;
+ uint32_t owners_line_number;
+ TranslateLocation(owners_method, owners_dex_pc, &owners_filename, &owners_line_number);
+ LogContentionEvent(self, wait_ms, sample_percent, owners_filename, owners_line_number);
+ }
+ }
}
}
- }
- owner_ = self;
- DCHECK_EQ(lock_count_, 0);
-
- // When debugging, save the current monitor holder for future
- // acquisition failures to use in sampled logging.
- if (lock_profiling_threshold_ != 0) {
- locking_method_ = self->GetCurrentMethod(&locking_dex_pc_);
+ monitor_lock_.Lock(self); // Reacquire locks in order.
}
}
@@ -257,11 +255,11 @@ static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...)
Thread* self = Thread::Current();
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewExceptionV(throw_location, "Ljava/lang/IllegalMonitorStateException;", fmt, args);
- if (!Runtime::Current()->IsStarted()) {
+ if (!Runtime::Current()->IsStarted() || VLOG_IS_ON(monitor)) {
std::ostringstream ss;
self->Dump(ss);
- std::string str(ss.str());
- LOG(ERROR) << "IllegalMonitorStateException: " << str;
+ LOG(Runtime::Current()->IsStarted() ? INFO : ERROR)
+ << self->GetException(NULL)->Dump() << "\n" << ss.str();
}
va_end(args);
}
@@ -287,7 +285,7 @@ void Monitor::FailedUnlock(mirror::Object* o, Thread* expected_owner, Thread* fo
// Acquire thread list lock so threads won't disappear from under us.
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
// Re-read owner now that we hold lock.
- current_owner = (monitor != NULL) ? monitor->owner_ : NULL;
+ current_owner = (monitor != NULL) ? monitor->GetOwner() : NULL;
// Get short descriptions of the threads involved.
current_owner_string = ThreadToString(current_owner);
expected_owner_string = ThreadToString(expected_owner);
@@ -335,8 +333,9 @@ void Monitor::FailedUnlock(mirror::Object* o, Thread* expected_owner, Thread* fo
}
}
-bool Monitor::Unlock(Thread* self, bool for_wait) {
+bool Monitor::Unlock(Thread* self) {
DCHECK(self != NULL);
+ MutexLock mu(self, monitor_lock_);
Thread* owner = owner_;
if (owner == self) {
// We own the monitor, so nobody else can be in here.
@@ -344,17 +343,11 @@ bool Monitor::Unlock(Thread* self, bool for_wait) {
owner_ = NULL;
locking_method_ = NULL;
locking_dex_pc_ = 0;
- monitor_lock_.Unlock(self);
+ // Wake a contender.
+ monitor_contenders_.Signal(self);
} else {
--lock_count_;
}
- } else if (for_wait) {
- // Wait should have already cleared the fields.
- DCHECK_EQ(lock_count_, 0);
- DCHECK(owner == NULL);
- DCHECK(locking_method_ == NULL);
- DCHECK_EQ(locking_dex_pc_, 0u);
- monitor_lock_.Unlock(self);
} else {
// We don't own this, so we're not allowed to unlock it.
// The JNI spec says that we should throw IllegalMonitorStateException
@@ -393,12 +386,14 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
DCHECK(self != NULL);
DCHECK(why == kTimedWaiting || why == kWaiting || why == kSleeping);
+ monitor_lock_.Lock(self);
+
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()");
+ monitor_lock_.Unlock(self);
return;
}
- monitor_lock_.AssertHeld(self);
// We need to turn a zero-length timed wait into a regular wait because
// Object.wait(0, 0) is defined as Object.wait(0), which is defined as Object.wait().
@@ -406,16 +401,12 @@ void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
why = kWaiting;
}
- WaitWithLock(self, ms, ns, interruptShouldThrow, why);
-}
-
-void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns,
- bool interruptShouldThrow, ThreadState why) {
// Enforce the timeout range.
if (ms < 0 || ns < 0 || ns > 999999) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;",
"timeout arguments out of range: ms=%lld ns=%d", ms, ns);
+ monitor_lock_.Unlock(self);
return;
}
@@ -457,7 +448,8 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns,
self->wait_monitor_ = this;
// Release the monitor lock.
- Unlock(self, true);
+ monitor_contenders_.Signal(self);
+ monitor_lock_.Unlock(self);
// Handle the case where the thread was interrupted before we called wait().
if (self->interrupted_) {
@@ -490,9 +482,9 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns,
self->wait_monitor_ = NULL;
}
- // Re-acquire the monitor lock.
+ // Re-acquire the monitor and lock.
Lock(self);
-
+ monitor_lock_.Lock(self);
self->wait_mutex_->AssertNotHeld(self);
/*
@@ -524,20 +516,17 @@ void Monitor::WaitWithLock(Thread* self, int64_t ms, int32_t ns,
self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL);
}
}
+ monitor_lock_.Unlock(self);
}
void Monitor::Notify(Thread* self) {
DCHECK(self != NULL);
+ MutexLock mu(self, monitor_lock_);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
return;
}
- monitor_lock_.AssertHeld(self);
- NotifyWithLock(self);
-}
-
-void Monitor::NotifyWithLock(Thread* self) {
// Signal the first waiting thread in the wait set.
while (wait_set_ != NULL) {
Thread* thread = wait_set_;
@@ -555,16 +544,12 @@ void Monitor::NotifyWithLock(Thread* self) {
void Monitor::NotifyAll(Thread* self) {
DCHECK(self != NULL);
+ MutexLock mu(self, monitor_lock_);
// Make sure that we hold the lock.
if (owner_ != self) {
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()");
return;
}
- monitor_lock_.AssertHeld(self);
- NotifyAllWithLock();
-}
-
-void Monitor::NotifyAllWithLock() {
// Signal all threads in the wait set.
while (wait_set_ != NULL) {
Thread* thread = wait_set_;
@@ -575,182 +560,147 @@ void Monitor::NotifyAllWithLock() {
}
/*
- * Changes the shape of a monitor from thin to fat, preserving the
- * internal lock state. The calling thread must own the lock.
+ * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The calling
+ * thread must own the lock or the owner must be suspended. There's a race with other threads
+ * inflating the lock and so the caller should read the monitor following the call.
*/
-void Monitor::Inflate(Thread* self, mirror::Object* obj) {
+void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) {
DCHECK(self != NULL);
DCHECK(obj != NULL);
- DCHECK_EQ(LW_SHAPE(*obj->GetRawLockWordAddress()), LW_SHAPE_THIN);
- DCHECK_EQ(LW_LOCK_OWNER(*obj->GetRawLockWordAddress()), static_cast<int32_t>(self->GetThinLockId()));
-
// Allocate and acquire a new monitor.
- Monitor* m = new Monitor(self, obj);
- VLOG(monitor) << "monitor: thread " << self->GetThinLockId()
- << " created monitor " << m << " for object " << obj;
- Runtime::Current()->GetMonitorList()->Add(m);
+ UniquePtr<Monitor> m(new Monitor(owner, obj, hash_code));
+ if (m->Install(self)) {
+ VLOG(monitor) << "monitor: thread " << owner->GetThreadId()
+ << " created monitor " << m.get() << " for object " << obj;
+ Runtime::Current()->GetMonitorList()->Add(m.release());
+ CHECK_EQ(obj->GetLockWord().GetState(), LockWord::kFatLocked);
+ }
}
-void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) {
- volatile int32_t* thinp = obj->GetRawLockWordAddress();
- uint32_t sleepDelayNs;
- uint32_t minSleepDelayNs = 1000000; /* 1 millisecond */
- uint32_t maxSleepDelayNs = 1000000000; /* 1 second */
- uint32_t thin, newThin;
+void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word,
+ uint32_t hash_code) {
+ DCHECK_EQ(lock_word.GetState(), LockWord::kThinLocked);
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id == self->GetThreadId()) {
+ // We own the monitor, we can easily inflate it.
+ Inflate(self, self, obj, hash_code);
+ } else {
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
+ // Suspend the owner, inflate. First change to blocked and give up mutator_lock_.
+ ScopedThreadStateChange tsc(self, kBlocked);
+ if (lock_word == obj->GetLockWord()) { // If lock word hasn't changed.
+ bool timed_out;
+ Thread* owner = thread_list->SuspendThreadByThreadId(lock_word.ThinLockOwner(), false,
+ &timed_out);
+ if (owner != nullptr) {
+ // We succeeded in suspending the thread, check the lock's status didn't change.
+ lock_word = obj->GetLockWord();
+ if (lock_word.GetState() == LockWord::kThinLocked &&
+ lock_word.ThinLockOwner() == owner_thread_id) {
+ // Go ahead and inflate the lock.
+ Inflate(self, owner, obj, hash_code);
+ }
+ thread_list->Resume(owner, false);
+ }
+ }
+ }
+}
+void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) {
DCHECK(self != NULL);
DCHECK(obj != NULL);
- uint32_t threadId = self->GetThinLockId();
- retry:
- thin = *thinp;
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- /*
- * The lock is a thin lock. The owner field is used to
- * determine the acquire method, ordered by cost.
- */
- if (LW_LOCK_OWNER(thin) == threadId) {
- /*
- * The calling thread owns the lock. Increment the
- * value of the recursion count field.
- */
- *thinp += 1 << LW_LOCK_COUNT_SHIFT;
- if (LW_LOCK_COUNT(*thinp) == LW_LOCK_COUNT_MASK) {
- /*
- * The reacquisition limit has been reached. Inflate
- * the lock so the next acquire will not overflow the
- * recursion count field.
- */
- Inflate(self, obj);
- }
- } else if (LW_LOCK_OWNER(thin) == 0) {
- // The lock is unowned. Install the thread id of the calling thread into the owner field.
- // This is the common case: compiled code will have tried this before calling back into
- // the runtime.
- newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
- if (android_atomic_acquire_cas(thin, newThin, thinp) != 0) {
- // The acquire failed. Try again.
- goto retry;
+ uint32_t thread_id = self->GetThreadId();
+ size_t contention_count = 0;
+ while (true) {
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kUnlocked: {
+ LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0));
+ if (obj->CasLockWord(lock_word, thin_locked)) {
+ return; // Success!
+ }
+ continue; // Go again.
}
- } else {
- VLOG(monitor) << StringPrintf("monitor: thread %d spin on lock %p (a %s) owned by %d",
- threadId, thinp, PrettyTypeOf(obj).c_str(), LW_LOCK_OWNER(thin));
- // The lock is owned by another thread. Notify the runtime that we are about to wait.
- self->monitor_enter_object_ = obj;
- self->TransitionFromRunnableToSuspended(kBlocked);
- // Spin until the thin lock is released or inflated.
- sleepDelayNs = 0;
- for (;;) {
- thin = *thinp;
- // Check the shape of the lock word. Another thread
- // may have inflated the lock while we were waiting.
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- if (LW_LOCK_OWNER(thin) == 0) {
- // The lock has been released. Install the thread id of the
- // calling thread into the owner field.
- newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
- if (android_atomic_acquire_cas(thin, newThin, thinp) == 0) {
- // The acquire succeed. Break out of the loop and proceed to inflate the lock.
- break;
- }
+ case LockWord::kThinLocked: {
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id == thread_id) {
+ // We own the lock, increase the recursion count.
+ uint32_t new_count = lock_word.ThinLockCount() + 1;
+ if (LIKELY(new_count <= LockWord::kThinLockMaxCount)) {
+ LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count));
+ obj->SetLockWord(thin_locked);
+ return; // Success!
} else {
- // The lock has not been released. Yield so the owning thread can run.
- if (sleepDelayNs == 0) {
- sched_yield();
- sleepDelayNs = minSleepDelayNs;
- } else {
- NanoSleep(sleepDelayNs);
- // Prepare the next delay value. Wrap to avoid once a second polls for eternity.
- if (sleepDelayNs < maxSleepDelayNs / 2) {
- sleepDelayNs *= 2;
- } else {
- sleepDelayNs = minSleepDelayNs;
- }
- }
+ // We'd overflow the recursion count, so inflate the monitor.
+ InflateThinLocked(self, obj, lock_word, 0);
}
} else {
- // The thin lock was inflated by another thread. Let the runtime know we are no longer
- // waiting and try again.
- VLOG(monitor) << StringPrintf("monitor: thread %d found lock %p surprise-fattened by another thread", threadId, thinp);
- self->monitor_enter_object_ = NULL;
- self->TransitionFromSuspendedToRunnable();
- goto retry;
+ // Contention.
+ contention_count++;
+ Runtime* runtime = Runtime::Current();
+ if (contention_count <= runtime->GetMaxSpinsBeforeThinkLockInflation()) {
+ NanoSleep(1000); // Sleep for 1us and re-attempt.
+ } else {
+ contention_count = 0;
+ InflateThinLocked(self, obj, lock_word, 0);
+ }
}
+ continue; // Start from the beginning.
+ }
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ mon->Lock(self);
+ return; // Success!
+ }
+ case LockWord::kHashCode: {
+ // Inflate with the existing hashcode.
+ Inflate(self, nullptr, obj, lock_word.GetHashCode());
+ break;
}
- VLOG(monitor) << StringPrintf("monitor: thread %d spin on lock %p done", threadId, thinp);
- // We have acquired the thin lock. Let the runtime know that we are no longer waiting.
- self->monitor_enter_object_ = NULL;
- self->TransitionFromSuspendedToRunnable();
- // Fatten the lock.
- Inflate(self, obj);
- VLOG(monitor) << StringPrintf("monitor: thread %d fattened lock %p", threadId, thinp);
}
- } else {
- // The lock is a fat lock.
- VLOG(monitor) << StringPrintf("monitor: thread %d locking fat lock %p (%p) %p on a %s",
- threadId, thinp, LW_MONITOR(*thinp),
- reinterpret_cast<void*>(*thinp), PrettyTypeOf(obj).c_str());
- DCHECK(LW_MONITOR(*thinp) != NULL);
- LW_MONITOR(*thinp)->Lock(self);
}
}
bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) {
- volatile int32_t* thinp = obj->GetRawLockWordAddress();
-
DCHECK(self != NULL);
- // DCHECK_EQ(self->GetState(), kRunnable);
DCHECK(obj != NULL);
- /*
- * Cache the lock word as its value can change while we are
- * examining its state.
- */
- uint32_t thin = *thinp;
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- /*
- * The lock is thin. We must ensure that the lock is owned
- * by the given thread before unlocking it.
- */
- if (LW_LOCK_OWNER(thin) == self->GetThinLockId()) {
- /*
- * We are the lock owner. It is safe to update the lock
- * without CAS as lock ownership guards the lock itself.
- */
- if (LW_LOCK_COUNT(thin) == 0) {
- /*
- * The lock was not recursively acquired, the common
- * case. Unlock by clearing all bits except for the
- * hash state.
- */
- thin &= (LW_HASH_STATE_MASK << LW_HASH_STATE_SHIFT);
- android_atomic_release_store(thin, thinp);
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
+ FailedUnlock(obj, self, NULL, NULL);
+ return false; // Failure.
+ case LockWord::kThinLocked: {
+ uint32_t thread_id = self->GetThreadId();
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id != thread_id) {
+ // TODO: there's a race here with the owner dying while we unlock.
+ Thread* owner =
+ Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+ FailedUnlock(obj, self, owner, NULL);
+ return false; // Failure.
} else {
- /*
- * The object was recursively acquired. Decrement the
- * lock recursion count field.
- */
- *thinp -= 1 << LW_LOCK_COUNT_SHIFT;
+ // We own the lock, decrease the recursion count.
+ if (lock_word.ThinLockCount() != 0) {
+ uint32_t new_count = lock_word.ThinLockCount() - 1;
+ LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count));
+ obj->SetLockWord(thin_locked);
+ } else {
+ obj->SetLockWord(LockWord());
+ }
+ return true; // Success!
}
- } else {
- /*
- * We do not own the lock. The JVM spec requires that we
- * throw an exception in this case.
- */
- FailedUnlock(obj, self, NULL, NULL);
- return false;
}
- } else {
- /*
- * The lock is fat. We must check to see if Unlock has
- * raised any exceptions before continuing.
- */
- DCHECK(LW_MONITOR(*thinp) != NULL);
- if (!LW_MONITOR(*thinp)->Unlock(self, false)) {
- // An exception has been raised. Do not fall through.
- return false;
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ return mon->Unlock(self);
}
+ default:
+ LOG(FATAL) << "Unreachable";
+ return false;
}
- return true;
}
/*
@@ -758,84 +708,97 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) {
*/
void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why) {
- volatile int32_t* thinp = obj->GetRawLockWordAddress();
+ DCHECK(self != NULL);
+ DCHECK(obj != NULL);
- // If the lock is still thin, we need to fatten it.
- uint32_t thin = *thinp;
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- // Make sure that 'self' holds the lock.
- if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()");
- return;
+ return; // Failure.
+ case LockWord::kThinLocked: {
+ uint32_t thread_id = self->GetThreadId();
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id != thread_id) {
+ ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()");
+ return; // Failure.
+ } else {
+ // We own the lock, inflate to enqueue ourself on the Monitor.
+ Inflate(self, self, obj, 0);
+ lock_word = obj->GetLockWord();
+ }
+ break;
}
-
- /* This thread holds the lock. We need to fatten the lock
- * so 'self' can block on it. Don't update the object lock
- * field yet, because 'self' needs to acquire the lock before
- * any other thread gets a chance.
- */
- Inflate(self, obj);
- VLOG(monitor) << StringPrintf("monitor: thread %d fattened lock %p by wait()", self->GetThinLockId(), thinp);
+ case LockWord::kFatLocked:
+ break; // Already set for a wait.
}
- LW_MONITOR(*thinp)->Wait(self, ms, ns, interruptShouldThrow, why);
+ Monitor* mon = lock_word.FatLockMonitor();
+ mon->Wait(self, ms, ns, interruptShouldThrow, why);
}
-void Monitor::Notify(Thread* self, mirror::Object *obj) {
- uint32_t thin = *obj->GetRawLockWordAddress();
+void Monitor::DoNotify(Thread* self, mirror::Object* obj, bool notify_all) {
+ DCHECK(self != NULL);
+ DCHECK(obj != NULL);
- // If the lock is still thin, there aren't any waiters;
- // waiting on an object forces lock fattening.
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- // Make sure that 'self' holds the lock.
- if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
- return;
+ return; // Failure.
+ case LockWord::kThinLocked: {
+ uint32_t thread_id = self->GetThreadId();
+ uint32_t owner_thread_id = lock_word.ThinLockOwner();
+ if (owner_thread_id != thread_id) {
+ ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
+ return; // Failure.
+ } else {
+ // We own the lock but there's no Monitor and therefore no waiters.
+ return; // Success.
+ }
+ }
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ if (notify_all) {
+ mon->NotifyAll(self);
+ } else {
+ mon->Notify(self);
+ }
+ return; // Success.
}
- // no-op; there are no waiters to notify.
- // We inflate here in case the Notify is in a tight loop. Without inflation here the waiter
- // will struggle to get in. Bug 6961405.
- Inflate(self, obj);
- } else {
- // It's a fat lock.
- LW_MONITOR(thin)->Notify(self);
}
}
-void Monitor::NotifyAll(Thread* self, mirror::Object *obj) {
- uint32_t thin = *obj->GetRawLockWordAddress();
+uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) {
+ DCHECK(obj != NULL);
- // If the lock is still thin, there aren't any waiters;
- // waiting on an object forces lock fattening.
- if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
- // Make sure that 'self' holds the lock.
- if (LW_LOCK_OWNER(thin) != self->GetThinLockId()) {
- ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()");
- return;
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kHashCode:
+ // Fall-through.
+ case LockWord::kUnlocked:
+ return ThreadList::kInvalidThreadId;
+ case LockWord::kThinLocked:
+ return lock_word.ThinLockOwner();
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ return mon->GetOwnerThreadId();
}
- // no-op; there are no waiters to notify.
- // We inflate here in case the NotifyAll is in a tight loop. Without inflation here the waiter
- // will struggle to get in. Bug 6961405.
- Inflate(self, obj);
- } else {
- // It's a fat lock.
- LW_MONITOR(thin)->NotifyAll(self);
- }
-}
-
-uint32_t Monitor::GetThinLockId(uint32_t raw_lock_word) {
- if (LW_SHAPE(raw_lock_word) == LW_SHAPE_THIN) {
- return LW_LOCK_OWNER(raw_lock_word);
- } else {
- Thread* owner = LW_MONITOR(raw_lock_word)->owner_;
- return owner ? owner->GetThinLockId() : 0;
+ default:
+ LOG(FATAL) << "Unreachable";
+ return ThreadList::kInvalidThreadId;
}
}
void Monitor::DescribeWait(std::ostream& os, const Thread* thread) {
ThreadState state = thread->GetState();
- mirror::Object* object = NULL;
- uint32_t lock_owner = ThreadList::kInvalidId;
+ int32_t object_identity_hashcode = 0;
+ uint32_t lock_owner = ThreadList::kInvalidThreadId;
+ std::string pretty_type;
if (state == kWaiting || state == kTimedWaiting || state == kSleeping) {
if (state == kSleeping) {
os << " - sleeping on ";
@@ -847,14 +810,18 @@ void Monitor::DescribeWait(std::ostream& os, const Thread* thread) {
MutexLock mu(self, *thread->wait_mutex_);
Monitor* monitor = thread->wait_monitor_;
if (monitor != NULL) {
- object = monitor->obj_;
+ mirror::Object* object = monitor->obj_;
+ object_identity_hashcode = object->IdentityHashCode();
+ pretty_type = PrettyTypeOf(object);
}
}
} else if (state == kBlocked) {
os << " - waiting to lock ";
- object = thread->monitor_enter_object_;
+ mirror::Object* object = thread->monitor_enter_object_;
if (object != NULL) {
- lock_owner = object->GetThinLockId();
+ object_identity_hashcode = object->IdentityHashCode();
+ lock_owner = object->GetLockOwnerThreadId();
+ pretty_type = PrettyTypeOf(object);
}
} else {
// We're not waiting on anything.
@@ -862,10 +829,10 @@ void Monitor::DescribeWait(std::ostream& os, const Thread* thread) {
}
// - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
- os << "<" << object << "> (a " << PrettyTypeOf(object) << ")";
+ os << StringPrintf("<0x%08x> (a %s)", object_identity_hashcode, pretty_type.c_str());
// - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5
- if (lock_owner != ThreadList::kInvalidId) {
+ if (lock_owner != ThreadList::kInvalidThreadId) {
os << " held by thread " << lock_owner;
}
@@ -876,18 +843,15 @@ mirror::Object* Monitor::GetContendedMonitor(Thread* thread) {
// This is used to implement JDWP's ThreadReference.CurrentContendedMonitor, and has a bizarre
// definition of contended that includes a monitor a thread is trying to enter...
mirror::Object* result = thread->monitor_enter_object_;
- if (result != NULL) {
- return result;
- }
- // ...but also a monitor that the thread is waiting on.
- {
+ if (result == NULL) {
+ // ...but also a monitor that the thread is waiting on.
MutexLock mu(Thread::Current(), *thread->wait_mutex_);
Monitor* monitor = thread->wait_monitor_;
if (monitor != NULL) {
- return monitor->obj_;
+ result = monitor->GetObject();
}
}
- return NULL;
+ return result;
}
void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::Object*, void*),
@@ -952,41 +916,63 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O
}
}
-bool Monitor::IsValidLockWord(int32_t lock_word) {
- if (lock_word == 0) {
- return true;
- } else if (LW_SHAPE(lock_word) == LW_SHAPE_FAT) {
- Monitor* mon = LW_MONITOR(lock_word);
- MonitorList* list = Runtime::Current()->GetMonitorList();
- MutexLock mu(Thread::Current(), list->monitor_list_lock_);
- bool found = false;
- for (Monitor* list_mon : list->list_) {
- if (mon == list_mon) {
- found = true;
- break;
+bool Monitor::IsValidLockWord(LockWord lock_word) {
+ switch (lock_word.GetState()) {
+ case LockWord::kUnlocked:
+ // Nothing to check.
+ return true;
+ case LockWord::kThinLocked:
+ // Basic sanity check of owner.
+ return lock_word.ThinLockOwner() != ThreadList::kInvalidThreadId;
+ case LockWord::kFatLocked: {
+ // Check the monitor appears in the monitor list.
+ Monitor* mon = lock_word.FatLockMonitor();
+ MonitorList* list = Runtime::Current()->GetMonitorList();
+ MutexLock mu(Thread::Current(), list->monitor_list_lock_);
+ for (Monitor* list_mon : list->list_) {
+ if (mon == list_mon) {
+ return true; // Found our monitor.
+ }
}
+ return false; // Fail - unowned monitor in an object.
}
- return found;
- } else {
- // TODO: thin lock validity checking.
- return LW_SHAPE(lock_word) == LW_SHAPE_THIN;
+ case LockWord::kHashCode:
+ return true;
+ default:
+ LOG(FATAL) << "Unreachable";
+ return false;
}
}
+bool Monitor::IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ MutexLock mu(Thread::Current(), monitor_lock_);
+ return owner_ != nullptr;
+}
+
void Monitor::TranslateLocation(const mirror::ArtMethod* method, uint32_t dex_pc,
- const char*& source_file, uint32_t& line_number) const {
+ const char** source_file, uint32_t* line_number) const {
// If method is null, location is unknown
if (method == NULL) {
- source_file = "";
- line_number = 0;
+ *source_file = "";
+ *line_number = 0;
return;
}
MethodHelper mh(method);
- source_file = mh.GetDeclaringClassSourceFile();
- if (source_file == NULL) {
- source_file = "";
+ *source_file = mh.GetDeclaringClassSourceFile();
+ if (*source_file == NULL) {
+ *source_file = "";
+ }
+ *line_number = mh.GetLineNumFromDexPC(dex_pc);
+}
+
+uint32_t Monitor::GetOwnerThreadId() {
+ MutexLock mu(Thread::Current(), monitor_lock_);
+ Thread* owner = owner_;
+ if (owner != NULL) {
+ return owner->GetThreadId();
+ } else {
+ return ThreadList::kInvalidThreadId;
}
- line_number = mh.GetLineNumFromDexPC(dex_pc);
}
MonitorList::MonitorList()
@@ -1020,36 +1006,46 @@ void MonitorList::Add(Monitor* m) {
list_.push_front(m);
}
-void MonitorList::SweepMonitorList(IsMarkedTester is_marked, void* arg) {
+void MonitorList::SweepMonitorList(RootVisitor visitor, void* arg) {
MutexLock mu(Thread::Current(), monitor_list_lock_);
for (auto it = list_.begin(); it != list_.end(); ) {
Monitor* m = *it;
- if (!is_marked(m->GetObject(), arg)) {
- VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object " << m->GetObject();
+ mirror::Object* obj = m->GetObject();
+ mirror::Object* new_obj = visitor(obj, arg);
+ if (new_obj == nullptr) {
+ VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object "
+ << m->GetObject();
delete m;
it = list_.erase(it);
} else {
+ m->SetObject(new_obj);
++it;
}
}
}
-MonitorInfo::MonitorInfo(mirror::Object* o) : owner(NULL), entry_count(0) {
- uint32_t lock_word = *o->GetRawLockWordAddress();
- if (LW_SHAPE(lock_word) == LW_SHAPE_THIN) {
- uint32_t owner_thin_lock_id = LW_LOCK_OWNER(lock_word);
- if (owner_thin_lock_id != 0) {
- owner = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(owner_thin_lock_id);
- entry_count = 1 + LW_LOCK_COUNT(lock_word);
- }
- // Thin locks have no waiters.
- } else {
- CHECK_EQ(LW_SHAPE(lock_word), LW_SHAPE_FAT);
- Monitor* monitor = LW_MONITOR(lock_word);
- owner = monitor->owner_;
- entry_count = 1 + monitor->lock_count_;
- for (Thread* waiter = monitor->wait_set_; waiter != NULL; waiter = waiter->wait_next_) {
- waiters.push_back(waiter);
+MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) {
+ DCHECK(obj != NULL);
+
+ LockWord lock_word = obj->GetLockWord();
+ switch (lock_word.GetState()) {
+ case LockWord::kUnlocked:
+ // Fall-through.
+ case LockWord::kHashCode:
+ break;
+ case LockWord::kThinLocked:
+ owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+ entry_count_ = 1 + lock_word.ThinLockCount();
+ // Thin locks have no waiters.
+ break;
+ case LockWord::kFatLocked: {
+ Monitor* mon = lock_word.FatLockMonitor();
+ owner_ = mon->owner_;
+ entry_count_ = 1 + mon->lock_count_;
+ for (Thread* waiter = mon->wait_set_; waiter != NULL; waiter = waiter->wait_next_) {
+ waiters_.push_back(waiter);
+ }
+ break;
}
}
}
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 0b5b7e546a..09cfafa042 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -24,53 +24,35 @@
#include <list>
#include <vector>
+#include "atomic_integer.h"
#include "base/mutex.h"
#include "root_visitor.h"
#include "thread_state.h"
namespace art {
-/*
- * Monitor shape field. Used to distinguish thin locks from fat locks.
- */
-#define LW_SHAPE_THIN 0
-#define LW_SHAPE_FAT 1
-
-/*
- * Hash state field. Used to signify that an object has had its
- * identity hash code exposed or relocated.
- */
-#define LW_HASH_STATE_UNHASHED 0
-#define LW_HASH_STATE_HASHED 1
-#define LW_HASH_STATE_HASHED_AND_MOVED 3
-#define LW_HASH_STATE_MASK 0x3
-#define LW_HASH_STATE_SHIFT 1
-#define LW_HASH_STATE(x) (((x) >> LW_HASH_STATE_SHIFT) & LW_HASH_STATE_MASK)
-
-/*
- * Lock owner field. Contains the thread id of the thread currently
- * holding the lock.
- */
-#define LW_LOCK_OWNER_MASK 0xffff
-#define LW_LOCK_OWNER_SHIFT 3
-#define LW_LOCK_OWNER(x) (((x) >> LW_LOCK_OWNER_SHIFT) & LW_LOCK_OWNER_MASK)
-
namespace mirror {
class ArtMethod;
class Object;
} // namespace mirror
+class LockWord;
class Thread;
class StackVisitor;
class Monitor {
public:
+ // The default number of spins that are done before thread suspension is used to forcibly inflate
+ // a lock word. See Runtime::max_spins_before_thin_lock_inflation_.
+ constexpr static size_t kDefaultMaxSpinsBeforeThinLockInflation = 50;
+
~Monitor();
static bool IsSensitiveThread();
static void Init(uint32_t lock_profiling_threshold, bool (*is_sensitive_thread_hook)());
- static uint32_t GetThinLockId(uint32_t raw_lock_word)
- NO_THREAD_SAFETY_ANALYSIS; // Reading lock owner without holding lock is racy.
+ // Return the thread id of the lock owner or 0 when there is no owner.
+ static uint32_t GetLockOwnerThreadId(mirror::Object* obj)
+ NO_THREAD_SAFETY_ANALYSIS; // TODO: Reading lock owner without holding lock is racy.
static void MonitorEnter(Thread* thread, mirror::Object* obj)
EXCLUSIVE_LOCK_FUNCTION(monitor_lock_)
@@ -80,9 +62,13 @@ class Monitor {
UNLOCK_FUNCTION(monitor_lock_);
static void Notify(Thread* self, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DoNotify(self, obj, false);
+ }
static void NotifyAll(Thread* self, mirror::Object* obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ DoNotify(self, obj, true);
+ }
static void Wait(Thread* self, mirror::Object* obj, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -92,7 +78,8 @@ class Monitor {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Used to implement JDWP's ThreadReference.CurrentContendedMonitor.
- static mirror::Object* GetContendedMonitor(Thread* thread);
+ static mirror::Object* GetContendedMonitor(Thread* thread)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Calls 'callback' once for each lock held in the single stack frame represented by
// the current state of 'stack_visitor'.
@@ -100,18 +87,43 @@ class Monitor {
void* callback_context)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static bool IsValidLockWord(int32_t lock_word);
+ static bool IsValidLockWord(LockWord lock_word);
+
+ mirror::Object* GetObject() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return obj_;
+ }
+
+ void SetObject(mirror::Object* object);
- mirror::Object* GetObject();
+ Thread* GetOwner() const NO_THREAD_SAFETY_ANALYSIS {
+ return owner_;
+ }
+
+ int32_t GetHashCode();
+
+ bool IsLocked() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool HasHashCode() const {
+ return hash_code_.load() != 0;
+ }
+
+ static void InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word,
+ uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS;
private:
- explicit Monitor(Thread* owner, mirror::Object* obj)
+ explicit Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Install the monitor into its object, may fail if another thread installs a different monitor
+ // first.
+ bool Install(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_);
- static void Inflate(Thread* self, mirror::Object* obj)
+ static void Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample_percent,
@@ -122,48 +134,57 @@ class Monitor {
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Lock(Thread* self) EXCLUSIVE_LOCK_FUNCTION(monitor_lock_);
- bool Unlock(Thread* thread, bool for_wait) UNLOCK_FUNCTION(monitor_lock_);
+ void Lock(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool Unlock(Thread* thread)
+ LOCKS_EXCLUDED(monitor_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static void DoNotify(Thread* self, mirror::Object* obj, bool notify_all)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void Notify(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
- void NotifyWithLock(Thread* self)
- EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_)
+ void Notify(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void NotifyAll(Thread* self) NO_THREAD_SAFETY_ANALYSIS;
- void NotifyAllWithLock()
- EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_)
+ void NotifyAll(Thread* self)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Wait(Thread* self, int64_t msec, int32_t nsec, bool interruptShouldThrow, ThreadState why)
- NO_THREAD_SAFETY_ANALYSIS;
- void WaitWithLock(Thread* self, int64_t ms, int32_t ns, bool interruptShouldThrow, ThreadState why)
- EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_)
+ LOCKS_EXCLUDED(monitor_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Translates the provided method and pc into its declaring class' source file and line number.
void TranslateLocation(const mirror::ArtMethod* method, uint32_t pc,
- const char*& source_file, uint32_t& line_number) const
+ const char** source_file, uint32_t* line_number) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint32_t GetOwnerThreadId();
+
static bool (*is_sensitive_thread_hook_)();
static uint32_t lock_profiling_threshold_;
Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ ConditionVariable monitor_contenders_ GUARDED_BY(monitor_lock_);
// Which thread currently owns the lock?
- Thread* volatile owner_;
+ Thread* volatile owner_ GUARDED_BY(monitor_lock_);
// Owner's recursive lock depth.
int lock_count_ GUARDED_BY(monitor_lock_);
- // What object are we part of (for debugging).
- mirror::Object* const obj_;
+ // What object are we part of.
+ mirror::Object* obj_;
// Threads currently waiting on this monitor.
Thread* wait_set_ GUARDED_BY(monitor_lock_);
+ // Stored object hash code, generated lazily by GetHashCode.
+ AtomicInteger hash_code_;
+
// Method and dex pc where the lock owner acquired the lock, used when lock
// sampling is enabled. locking_method_ may be null if the lock is currently
// unlocked, or if the lock is acquired by the system when the stack is empty.
@@ -182,10 +203,11 @@ class MonitorList {
~MonitorList();
void Add(Monitor* m);
- void SweepMonitorList(IsMarkedTester is_marked, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
+
+ void SweepMonitorList(RootVisitor visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void DisallowNewMonitors();
void AllowNewMonitors();
+
private:
bool allow_new_monitors_ GUARDED_BY(monitor_list_lock_);
Mutex monitor_list_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -203,9 +225,9 @@ class MonitorInfo {
public:
explicit MonitorInfo(mirror::Object* o) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
- Thread* owner;
- size_t entry_count;
- std::vector<Thread*> waiters;
+ Thread* owner_;
+ size_t entry_count_;
+ std::vector<Thread*> waiters_;
private:
DISALLOW_COPY_AND_ASSIGN(MonitorInfo);
diff --git a/runtime/monitor_android.cc b/runtime/monitor_android.cc
index 8efa0721e8..d89290b836 100644
--- a/runtime/monitor_android.cc
+++ b/runtime/monitor_android.cc
@@ -81,7 +81,7 @@ void Monitor::LogContentionEvent(Thread* self, uint32_t wait_ms, uint32_t sample
mirror::ArtMethod* m = self->GetCurrentMethod(&pc);
const char* filename;
uint32_t line_number;
- TranslateLocation(m, pc, filename, line_number);
+ TranslateLocation(m, pc, &filename, &line_number);
cp = EventLogWriteString(cp, filename, strlen(filename));
// Emit the source code line number, 5 bytes.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 823013a950..ab5eab3955 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -89,43 +89,40 @@ static jint DexFile_openDexFileNative(JNIEnv* env, jclass, jstring javaSourceNam
if (sourceName.c_str() == NULL) {
return 0;
}
- std::string dex_location(sourceName.c_str());
NullableScopedUtfChars outputName(env, javaOutputName);
if (env->ExceptionCheck()) {
return 0;
}
- ScopedObjectAccess soa(env);
uint32_t dex_location_checksum;
- if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
- LOG(WARNING) << "Failed to compute checksum: " << dex_location;
- ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
- soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;",
- "Unable to get checksum of dex file: %s", dex_location.c_str());
+ std::string error_msg;
+ if (!DexFile::GetChecksum(sourceName.c_str(), &dex_location_checksum, &error_msg)) {
+ ScopedObjectAccess soa(env);
+ DCHECK(!error_msg.empty());
+ ThrowIOException("%s", error_msg.c_str());
return 0;
}
ClassLinker* linker = Runtime::Current()->GetClassLinker();
const DexFile* dex_file;
- if (outputName.c_str() == NULL) {
- dex_file = linker->FindDexFileInOatFileFromDexLocation(dex_location, dex_location_checksum);
+ if (outputName.c_str() == nullptr) {
+ dex_file = linker->FindDexFileInOatFileFromDexLocation(sourceName.c_str(),
+ dex_location_checksum, &error_msg);
} else {
- std::string oat_location(outputName.c_str());
- dex_file = linker->FindOrCreateOatFileForDexLocation(dex_location, dex_location_checksum, oat_location);
+ dex_file = linker->FindOrCreateOatFileForDexLocation(sourceName.c_str(), dex_location_checksum,
+ outputName.c_str(), &error_msg);
}
- if (dex_file == NULL) {
- LOG(WARNING) << "Failed to open dex file: " << dex_location;
- ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
- soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/IOException;",
- "Unable to open dex file: %s", dex_location.c_str());
+ if (dex_file == nullptr) {
+ CHECK_EQ(env->ExceptionCheck(), JNI_TRUE);
return 0;
}
return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
}
-static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static const DexFile* toDexFile(int dex_file_address, JNIEnv* env) {
const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
- if (dex_file == NULL) {
+ if (UNLIKELY(dex_file == nullptr)) {
+ ScopedObjectAccess soa(env);
ThrowNullPointerException(NULL, "dex_file == null");
}
return dex_file;
@@ -133,11 +130,8 @@ static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Lock
static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
const DexFile* dex_file;
- {
- ScopedObjectAccess soa(env);
- dex_file = toDexFile(cookie);
- }
- if (dex_file == NULL) {
+ dex_file = toDexFile(cookie, env);
+ if (dex_file == nullptr) {
return;
}
if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
@@ -148,8 +142,7 @@ static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
jint cookie) {
- ScopedObjectAccess soa(env);
- const DexFile* dex_file = toDexFile(cookie);
+ const DexFile* dex_file = toDexFile(cookie, env);
if (dex_file == NULL) {
VLOG(class_linker) << "Failed to find dex_file";
return NULL;
@@ -165,6 +158,7 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j
VLOG(class_linker) << "Failed to find dex_class_def";
return NULL;
}
+ ScopedObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
class_linker->RegisterDexFile(*dex_file);
mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
@@ -176,12 +170,9 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j
static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
const DexFile* dex_file;
- {
- ScopedObjectAccess soa(env);
- dex_file = toDexFile(cookie);
- }
- if (dex_file == NULL) {
- return NULL;
+ dex_file = toDexFile(cookie, env);
+ if (dex_file == nullptr) {
+ return nullptr;
}
std::vector<std::string> class_names;
@@ -194,21 +185,17 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
}
static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
- bool debug_logging = false;
+ const bool kVerboseLogging = false; // Spammy logging.
+ const bool kDebugLogging = true; // Logging useful for debugging.
ScopedUtfChars filename(env, javaFilename);
- if (filename.c_str() == NULL) {
- LOG(ERROR) << "DexFile_isDexOptNeeded null filename";
- return JNI_TRUE;
- }
- if (!OS::FileExists(filename.c_str())) {
+ if ((filename.c_str() == nullptr) || !OS::FileExists(filename.c_str())) {
LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist";
- ScopedObjectAccess soa(env);
- ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
- soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/io/FileNotFoundException;",
- "%s", filename.c_str());
- return JNI_TRUE;
+ ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
+ const char* message = (filename.c_str() == nullptr) ? "<empty file name>" : filename.c_str();
+ env->ThrowNew(fnfe.get(), message);
+ return JNI_FALSE;
}
// Always treat elements of the bootclasspath as up-to-date. The
@@ -218,7 +205,7 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename
const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath();
for (size_t i = 0; i < boot_class_path.size(); i++) {
if (boot_class_path[i]->GetLocation() == filename.c_str()) {
- if (debug_logging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename.c_str();
}
return JNI_FALSE;
@@ -227,40 +214,55 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename
// Check if we have an odex file next to the dex file.
std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename.c_str()));
- UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false));
- if (oat_file.get() != NULL) {
- ScopedObjectAccess soa(env);
- const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL);
- if (oat_dex_file == NULL) {
- if (debug_logging) {
- LOG(INFO) << "DexFile_isDexOptNeeded GetOatDexFile failed";
- }
- } else {
+ std::string error_msg;
+ UniquePtr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false,
+ &error_msg));
+ if (oat_file.get() == nullptr) {
+ if (kVerboseLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename.c_str()
+ << "': " << error_msg;
+ }
+ error_msg.clear();
+ } else {
+ const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename.c_str(), NULL,
+ kDebugLogging);
+ if (oat_dex_file != nullptr) {
uint32_t location_checksum;
- // If we have no classes.dex checksum such as in a user build, assume up-to-date.
- if (!DexFile::GetChecksum(filename.c_str(), &location_checksum)) {
- if (debug_logging) {
+ // If its not possible to read the classes.dex assume up-to-date as we won't be able to
+ // compile it anyway.
+ if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: "
- << filename.c_str();
+ << filename.c_str() << ": " << error_msg;
}
return JNI_FALSE;
}
- if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) {
- if (debug_logging) {
+ if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum,
+ &error_msg)) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename
- << " is up-to-date checksum compared to " << filename.c_str();
+ << " has an up-to-date checksum compared to " << filename.c_str();
}
return JNI_FALSE;
+ } else {
+ if (kVerboseLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename
+ << " with an out-of-date checksum compared to " << filename.c_str()
+ << ": " << error_msg;
+ }
+ error_msg.clear();
}
}
}
// Check if we have an oat file in the cache
std::string cache_location(GetDalvikCacheFilenameOrDie(filename.c_str()));
- oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false));
- if (oat_file.get() == NULL) {
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " does not exist for " << filename.c_str();
+ oat_file.reset(OatFile::Open(cache_location, filename.c_str(), NULL, false, &error_msg));
+ if (oat_file.get() == nullptr) {
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " does not exist for " << filename.c_str() << ": " << error_msg;
+ }
return JNI_TRUE;
}
@@ -268,41 +270,53 @@ static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename
if (space->IsImageSpace()) {
// TODO: Ensure this works with multiple image spaces.
const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader();
- if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() != image_header.GetOatChecksum()) {
- ScopedObjectAccess soa(env);
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " has out-of-date oat checksum compared to "
- << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() !=
+ image_header.GetOatChecksum()) {
+ if (kDebugLogging) {
+ ScopedObjectAccess soa(env);
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " has out-of-date oat checksum compared to "
+ << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ }
return JNI_TRUE;
}
if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
!= reinterpret_cast<uint32_t>(image_header.GetOatDataBegin())) {
- ScopedObjectAccess soa(env);
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " has out-of-date oat begin compared to "
- << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ if (kDebugLogging) {
+ ScopedObjectAccess soa(env);
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " has out-of-date oat begin compared to "
+ << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
+ }
return JNI_TRUE;
}
}
}
- ScopedObjectAccess soa(env);
uint32_t location_checksum;
- if (!DexFile::GetChecksum(filename.c_str(), &location_checksum)) {
- LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str();
+ if (!DexFile::GetChecksum(filename.c_str(), &location_checksum, &error_msg)) {
+ if (kDebugLogging) {
+ LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename.c_str()
+ << " (error " << error_msg << ")";
+ }
return JNI_TRUE;
}
- if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) {
- LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
- << " has out-of-date checksum compared to " << filename.c_str();
+ if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum,
+ &error_msg)) {
+ if (kDebugLogging) {
+ LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
+ << " has out-of-date checksum compared to " << filename.c_str()
+ << " (error " << error_msg << ")";
+ }
return JNI_TRUE;
}
- if (debug_logging) {
+ if (kVerboseLogging) {
LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
<< " is up-to-date for " << filename.c_str();
}
+ CHECK(error_msg.empty()) << error_msg;
return JNI_FALSE;
}
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 83d3acb339..aef000cf10 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -31,6 +31,7 @@
#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "object_utils.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "thread.h"
#include "thread_list.h"
@@ -56,7 +57,7 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env,
jobject,
jclass javaElementClass,
jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
#ifdef MOVING_GARBAGE_COLLECTOR
// TODO: right now, we don't have a copying collector, so there's no need
// to do anything special here, but we ought to pass the non-movability
@@ -87,7 +88,7 @@ static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) {
if (javaArray == NULL) { // Most likely allocation failed
return 0;
}
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
if (!array->IsArrayInstance()) {
ThrowIllegalArgumentException(NULL, "not an array");
@@ -153,21 +154,21 @@ static void VMRuntime_setTargetSdkVersionNative(JNIEnv* env, jobject, jint targe
}
static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) {
- ScopedObjectAccess soa(env);
- if (bytes < 0) {
+ if (UNLIKELY(bytes < 0)) {
+ ScopedObjectAccess soa(env);
ThrowRuntimeException("allocation size negative %d", bytes);
return;
}
- Runtime::Current()->GetHeap()->RegisterNativeAllocation(bytes);
+ Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, bytes);
}
static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
- ScopedObjectAccess soa(env);
- if (bytes < 0) {
+ if (UNLIKELY(bytes < 0)) {
+ ScopedObjectAccess soa(env);
ThrowRuntimeException("allocation size negative %d", bytes);
return;
}
- Runtime::Current()->GetHeap()->RegisterNativeFree(bytes);
+ Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes);
}
static void VMRuntime_trimHeap(JNIEnv*, jobject) {
@@ -175,10 +176,8 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) {
// Trim the managed heap.
gc::Heap* heap = Runtime::Current()->GetHeap();
- gc::space::DlMallocSpace* alloc_space = heap->GetAllocSpace();
- size_t alloc_space_size = alloc_space->Size();
- float managed_utilization =
- static_cast<float>(alloc_space->GetBytesAllocated()) / alloc_space_size;
+ float managed_utilization = (static_cast<float>(heap->GetBytesAllocated()) /
+ heap->GetTotalMemory());
size_t managed_reclaimed = heap->Trim();
uint64_t gc_heap_end_ns = NanoTime();
@@ -198,17 +197,18 @@ static void VMRuntime_trimHeap(JNIEnv*, jobject) {
}
static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
- Thread* self = static_cast<JNIEnvExt*>(env)->self;
+ Thread* self = ThreadForEnv(env);
Runtime::Current()->GetHeap()->ConcurrentGC(self);
}
typedef std::map<std::string, mirror::String*> StringTable;
-static void PreloadDexCachesStringsVisitor(const mirror::Object* root, void* arg) {
+static mirror::Object* PreloadDexCachesStringsVisitor(mirror::Object* root, void* arg) {
StringTable& table = *reinterpret_cast<StringTable*>(arg);
mirror::String* string = const_cast<mirror::Object*>(root)->AsString();
// LOG(INFO) << "VMRuntime.preloadDexCaches interned=" << string->ToModifiedUtf8();
table[string->ToModifiedUtf8()] = string;
+ return root;
}
// Based on ClassLinker::ResolveString.
@@ -221,8 +221,7 @@ static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache,
return;
}
const DexFile* dex_file = dex_cache->GetDexFile();
- uint32_t utf16Size;
- const char* utf8 = dex_file->StringDataAndLengthByIdx(string_idx, &utf16Size);
+ const char* utf8 = dex_file->StringDataByIdx(string_idx);
string = strings[utf8];
if (string == NULL) {
return;
@@ -508,7 +507,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
@@ -517,7 +516,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
- NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+ NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index eaf67b8f02..f91536544a 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -20,6 +20,7 @@
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "thread_list.h"
@@ -34,7 +35,7 @@ static jobject GetThreadStack(JNIEnv* env, jobject peer) {
}
// Suspend thread to build stack trace.
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out);
+ Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
if (thread != NULL) {
jobject trace;
{
@@ -42,7 +43,7 @@ static jobject GetThreadStack(JNIEnv* env, jobject peer) {
trace = thread->CreateInternalStackTrace(soa);
}
// Restart suspended thread.
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ Runtime::Current()->GetThreadList()->Resume(thread, false);
return trace;
} else {
if (timed_out) {
@@ -66,7 +67,7 @@ static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThre
// Returns the defining class loader of the caller's caller.
static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
NthCallerVisitor visitor(soa.Self(), 2);
visitor.WalkStack();
return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
@@ -93,7 +94,7 @@ static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject ja
mirror::Object* system;
mirror::Object* class_loader;
};
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* bootstrap = soa.Decode<mirror::Object*>(javaBootstrap);
mirror::Object* system = soa.Decode<mirror::Object*>(javaSystem);
ClosestUserClassLoaderVisitor visitor(soa.Self(), bootstrap, system);
@@ -103,7 +104,7 @@ static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject ja
// Returns the class of the caller's caller's caller.
static jclass VMStack_getStackClass2(JNIEnv* env, jclass) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
NthCallerVisitor visitor(soa.Self(), 3);
visitor.WalkStack();
return soa.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass());
@@ -119,9 +120,9 @@ static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject jav
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"),
- NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"),
- NATIVE_METHOD(VMStack, getClosestUserClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
- NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"),
+ NATIVE_METHOD(VMStack, getCallingClassLoader, "!()Ljava/lang/ClassLoader;"),
+ NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
+ NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"),
NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"),
};
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index d3011cb013..3591611185 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -24,13 +24,14 @@
#include "mirror/proxy.h"
#include "object_utils.h"
#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
#include "well_known_classes.h"
namespace art {
-static mirror::Class* DecodeClass(const ScopedObjectAccess& soa, jobject java_class)
+static mirror::Class* DecodeClass(const ScopedFastNativeObjectAccess& soa, jobject java_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
DCHECK(c != NULL);
@@ -79,13 +80,13 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean
}
static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Class* c = DecodeClass(soa, javaThis);
return soa.AddLocalReference<jstring>(c->ComputeName());
}
static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::SynthesizedProxyClass* c =
down_cast<mirror::SynthesizedProxyClass*>(DecodeClass(soa, javaThis));
return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
@@ -93,8 +94,8 @@ static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
- NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
- NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"),
+ NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"),
+ NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"),
};
void register_java_lang_Class(JNIEnv* env) {
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index f8eeb2906e..51cd5b80d5 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -17,16 +17,16 @@
#include "dex_file.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "well_known_classes.h"
namespace art {
static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
// Should only be called while holding the lock on the dex cache.
- DCHECK_EQ(dex_cache->GetThinLockId(), soa.Self()->GetThinLockId());
+ DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId());
const DexFile* dex_file = dex_cache->GetDexFile();
if (dex_file == NULL) {
return NULL;
@@ -46,7 +46,7 @@ static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DexCache, getDexNative, "()Lcom/android/dex/Dex;"),
+ NATIVE_METHOD(DexCache, getDexNative, "!()Lcom/android/dex/Dex;"),
};
void register_java_lang_DexCache(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index 5db7a330a8..4768f48d9c 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -16,7 +16,7 @@
#include "jni_internal.h"
#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
// TODO: better support for overloading.
#undef NATIVE_METHOD
@@ -26,41 +26,41 @@
namespace art {
static jobject Object_internalClone(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
return soa.AddLocalReference<jobject>(o->Clone(soa.Self()));
}
static void Object_notify(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->Notify(soa.Self());
}
static void Object_notifyAll(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->NotifyAll(soa.Self());
}
static void Object_wait(JNIEnv* env, jobject java_this) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->Wait(soa.Self());
}
static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
o->Wait(soa.Self(), ms, ns);
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;", internalClone),
- NATIVE_METHOD(Object, notify, "()V", notify),
- NATIVE_METHOD(Object, notifyAll, "()V", notifyAll),
- NATIVE_METHOD(Object, wait, "()V", wait),
- NATIVE_METHOD(Object, wait, "(JI)V", waitJI),
+ NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;", internalClone),
+ NATIVE_METHOD(Object, notify, "!()V", notify),
+ NATIVE_METHOD(Object, notifyAll, "!()V", notifyAll),
+ NATIVE_METHOD(Object, wait, "!()V", wait),
+ NATIVE_METHOD(Object, wait, "!(JI)V", waitJI),
};
void register_java_lang_Object(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index 55575cf5a2..e969fcf503 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -41,7 +41,6 @@ static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
}
static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) {
- ScopedObjectAccess soa(env);
ScopedUtfChars filename(env, javaFilename);
if (filename.c_str() == NULL) {
return NULL;
@@ -62,12 +61,15 @@ static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, job
}
}
- mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader);
std::string detail;
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, detail);
- if (success) {
- return NULL;
+ {
+ ScopedObjectAccess soa(env);
+ mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader);
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail);
+ if (success) {
+ return nullptr;
+ }
}
// Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 3e9c3f36fa..c401d502ff 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -17,13 +17,14 @@
#include "common_throws.h"
#include "jni_internal.h"
#include "mirror/string.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedLocalRef.h"
namespace art {
static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
if (UNLIKELY(javaRhs == NULL)) {
ThrowNullPointerException(NULL, "rhs == null");
return -1;
@@ -33,7 +34,7 @@ static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
}
static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
// This method does not handle supplementary characters. They're dealt with in managed code.
DCHECK_LE(ch, 0xffff);
@@ -42,16 +43,16 @@ static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint sta
}
static jstring String_intern(JNIEnv* env, jobject javaThis) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::String* s = soa.Decode<mirror::String*>(javaThis);
mirror::String* result = s->Intern();
return soa.AddLocalReference<jstring>(result);
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"),
- NATIVE_METHOD(String, fastIndexOf, "(II)I"),
- NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
+ NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"),
+ NATIVE_METHOD(String, fastIndexOf, "!(II)I"),
+ NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"),
};
void register_java_lang_String(JNIEnv* env) {
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 30b4dc7ef5..ea78e04702 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -22,7 +22,7 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
/*
* We make guarantees about the atomicity of accesses to primitive
@@ -179,7 +179,7 @@ static void ThrowArrayStoreException_NotAnArray(const char* identifier, mirror::
}
static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
// Null pointer checks.
if (UNLIKELY(javaSrc == NULL)) {
@@ -316,15 +316,41 @@ static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos,
}
}
+static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
+ ScopedFastNativeObjectAccess soa(env);
+ DCHECK(javaSrc != NULL);
+ DCHECK(javaDst != NULL);
+ mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc);
+ mirror::Object* dstObject = soa.Decode<mirror::Object*>(javaDst);
+ DCHECK(srcObject->IsArrayInstance());
+ DCHECK(dstObject->IsArrayInstance());
+ mirror::Array* srcArray = srcObject->AsArray();
+ mirror::Array* dstArray = dstObject->AsArray();
+ DCHECK(srcPos >= 0 && dstPos >= 0 && length >= 0 &&
+ srcPos + length <= srcArray->GetLength() && dstPos + length <= dstArray->GetLength());
+ DCHECK_EQ(srcArray->GetClass()->GetComponentType(), dstArray->GetClass()->GetComponentType());
+ DCHECK(srcArray->GetClass()->GetComponentType()->IsPrimitive());
+ DCHECK(dstArray->GetClass()->GetComponentType()->IsPrimitive());
+ DCHECK_EQ(srcArray->GetClass()->GetComponentSize(), static_cast<size_t>(2));
+ DCHECK_EQ(dstArray->GetClass()->GetComponentSize(), static_cast<size_t>(2));
+ uint8_t* dstBytes = reinterpret_cast<uint8_t*>(dstArray->GetRawData(2));
+ const uint8_t* srcBytes = reinterpret_cast<const uint8_t*>(srcArray->GetRawData(2));
+ move16(dstBytes + dstPos * 2, srcBytes + srcPos * 2, length * 2);
+}
+
static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
- ScopedObjectAccess soa(env);
+ if (javaObject == nullptr) {
+ return 0;
+ }
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* o = soa.Decode<mirror::Object*>(javaObject);
return static_cast<jint>(o->IdentityHashCode());
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
- NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"),
+ NATIVE_METHOD(System, arraycopy, "!(Ljava/lang/Object;ILjava/lang/Object;II)V"),
+ NATIVE_METHOD(System, arraycopyCharUnchecked, "!([CI[CII)V"),
+ NATIVE_METHOD(System, identityHashCode, "!(Ljava/lang/Object;)I"),
};
void register_java_lang_System(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index e85ef09a13..5b34cfb224 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -19,6 +19,7 @@
#include "jni_internal.h"
#include "monitor.h"
#include "mirror/object.h"
+#include "scoped_fast_native_object_access.h"
#include "scoped_thread_state_change.h"
#include "ScopedUtfChars.h"
#include "thread.h"
@@ -27,7 +28,7 @@
namespace art {
static jobject Thread_currentThread(JNIEnv* env, jclass) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
}
@@ -122,13 +123,13 @@ static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) {
// thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
// in the DDMS send code.
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer, true, &timed_out);
+ Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
if (thread != NULL) {
{
ScopedObjectAccess soa(env);
thread->SetThreadName(name.c_str());
}
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ Runtime::Current()->GetThreadList()->Resume(thread, false);
} else if (timed_out) {
LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
"failed to suspend within a generous timeout.";
@@ -150,7 +151,7 @@ static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_
}
static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock);
Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping);
}
@@ -166,7 +167,7 @@ static void Thread_yield(JNIEnv*, jobject) {
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"),
+ NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"),
NATIVE_METHOD(Thread, interrupted, "()Z"),
NATIVE_METHOD(Thread, isInterrupted, "()Z"),
NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
@@ -175,7 +176,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Thread, nativeInterrupt, "()V"),
NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),
- NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"),
NATIVE_METHOD(Thread, yield, "()V"),
};
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index c23b08ca66..af1b548aa3 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -72,8 +72,10 @@ static jstring VMClassLoader_getBootClassPathResource(JNIEnv* env, jclass, jstri
}
const DexFile* dex_file = path[index];
const std::string& location(dex_file->GetLocation());
- UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(location));
- if (zip_archive.get() == NULL) {
+ std::string error_msg;
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(location.c_str(), &error_msg));
+ if (zip_archive.get() == nullptr) {
+ LOG(WARNING) << "Failed to open zip archive '" << location << "': " << error_msg;
return NULL;
}
UniquePtr<ZipEntry> zip_entry(zip_archive->Find(name.c_str()));
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 45ec0ad5a2..a2d6b18026 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -21,13 +21,13 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "object_utils.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "sirt_ref.h"
namespace art {
static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
DCHECK(javaElementClass != NULL);
mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
DCHECK(element_class->IsClass());
@@ -41,7 +41,7 @@ static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementCla
}
static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
DCHECK(javaElementClass != NULL);
mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
if (UNLIKELY(length < 0)) {
@@ -63,8 +63,8 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Array, createMultiArray, "(Ljava/lang/Class;[I)Ljava/lang/Object;"),
- NATIVE_METHOD(Array, createObjectArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+ NATIVE_METHOD(Array, createMultiArray, "!(Ljava/lang/Class;[I)Ljava/lang/Object;"),
+ NATIVE_METHOD(Array, createObjectArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
};
void register_java_lang_reflect_Array(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 85556ac16e..aa72755c9d 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -35,6 +35,7 @@ namespace art {
* with an interface, array, or primitive class.
*/
static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
+ // TODO: ScopedFastNativeObjectAccess
ScopedObjectAccess soa(env);
jobject art_method = soa.Env()->GetObjectField(
javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod);
@@ -68,7 +69,7 @@ static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectA
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Constructor, newInstance, "([Ljava/lang/Object;)Ljava/lang/Object;"),
+ NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"),
};
void register_java_lang_reflect_Constructor(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 00f89b65ea..4d69a688ac 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -23,12 +23,12 @@
#include "mirror/class-inl.h"
#include "object_utils.h"
#include "reflection.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
namespace art {
-static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirror::ArtField* f,
- JValue& value, bool allow_references)
+static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o,
+ mirror::ArtField* f, JValue& value, bool allow_references)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK_EQ(value.GetJ(), 0LL);
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(),
@@ -77,8 +77,8 @@ static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirr
return false;
}
-static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::ArtField* f,
- mirror::Object*& class_or_rcvr)
+static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, jobject j_rcvr,
+ mirror::ArtField* f, mirror::Object*& class_or_rcvr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (f->IsStatic()) {
class_or_rcvr = f->GetDeclaringClass();
@@ -94,7 +94,7 @@ static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror:
}
static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
mirror::Object* o = NULL;
if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -112,7 +112,7 @@ static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
char dst_descriptor) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
mirror::Object* o = NULL;
if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -221,7 +221,7 @@ static void SetFieldValue(mirror::Object* o, mirror::ArtField* f, const JValue&
}
static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
// Unbox the value, if necessary.
@@ -242,7 +242,7 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j
static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
const JValue& new_value) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
mirror::Object* o = NULL;
if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -316,24 +316,24 @@ static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jsho
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Field, get, "(Ljava/lang/Object;)Ljava/lang/Object;"),
- NATIVE_METHOD(Field, getBoolean, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(Field, getByte, "(Ljava/lang/Object;)B"),
- NATIVE_METHOD(Field, getChar, "(Ljava/lang/Object;)C"),
- NATIVE_METHOD(Field, getDouble, "(Ljava/lang/Object;)D"),
- NATIVE_METHOD(Field, getFloat, "(Ljava/lang/Object;)F"),
- NATIVE_METHOD(Field, getInt, "(Ljava/lang/Object;)I"),
- NATIVE_METHOD(Field, getLong, "(Ljava/lang/Object;)J"),
- NATIVE_METHOD(Field, getShort, "(Ljava/lang/Object;)S"),
- NATIVE_METHOD(Field, set, "(Ljava/lang/Object;Ljava/lang/Object;)V"),
- NATIVE_METHOD(Field, setBoolean, "(Ljava/lang/Object;Z)V"),
- NATIVE_METHOD(Field, setByte, "(Ljava/lang/Object;B)V"),
- NATIVE_METHOD(Field, setChar, "(Ljava/lang/Object;C)V"),
- NATIVE_METHOD(Field, setDouble, "(Ljava/lang/Object;D)V"),
- NATIVE_METHOD(Field, setFloat, "(Ljava/lang/Object;F)V"),
- NATIVE_METHOD(Field, setInt, "(Ljava/lang/Object;I)V"),
- NATIVE_METHOD(Field, setLong, "(Ljava/lang/Object;J)V"),
- NATIVE_METHOD(Field, setShort, "(Ljava/lang/Object;S)V"),
+ NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;)Ljava/lang/Object;"),
+ NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;)B"),
+ NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"),
+ NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"),
+ NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;)F"),
+ NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;)I"),
+ NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;)S"),
+ NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;)V"),
+ NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"),
+ NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;B)V"),
+ NATIVE_METHOD(Field, setChar, "!(Ljava/lang/Object;C)V"),
+ NATIVE_METHOD(Field, setDouble, "!(Ljava/lang/Object;D)V"),
+ NATIVE_METHOD(Field, setFloat, "!(Ljava/lang/Object;F)V"),
+ NATIVE_METHOD(Field, setInt, "!(Ljava/lang/Object;I)V"),
+ NATIVE_METHOD(Field, setLong, "!(Ljava/lang/Object;J)V"),
+ NATIVE_METHOD(Field, setShort, "!(Ljava/lang/Object;S)V"),
};
void register_java_lang_reflect_Field(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index d7cd18dc9c..163ae20628 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,21 +16,21 @@
#include "base/logging.h"
#include "debugger.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
#include "ScopedPrimitiveArray.h"
namespace art {
static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type,
jbyteArray javaData, jint offset, jint length) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
ScopedByteArrayRO data(env, javaData);
DCHECK_LE(offset + length, static_cast<int32_t>(data.size()));
Dbg::DdmSendChunk(type, length, reinterpret_cast<const uint8_t*>(&data[offset]));
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DdmServer, nativeSendChunk, "(I[BII)V"),
+ NATIVE_METHOD(DdmServer, nativeSendChunk, "!(I[BII)V"),
};
void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 06769687bf..4f81a0b95a 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -44,22 +44,10 @@ static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) {
* NULL on failure, e.g. if the threadId couldn't be found.
*/
static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
- ScopedLocalRef<jobject> peer(env, NULL);
- {
- Thread* t = Runtime::Current()->GetThreadList()->FindThreadByThinLockId(thin_lock_id);
- if (t == NULL) {
- return NULL;
- }
- ScopedObjectAccess soa(env);
- peer.reset(soa.AddLocalReference<jobject>(t->GetPeer()));
- }
- if (peer.get() == NULL) {
- return NULL;
- }
-
// Suspend thread to build stack trace.
+ ThreadList* thread_list = Runtime::Current()->GetThreadList();
bool timed_out;
- Thread* thread = Thread::SuspendForDebugger(peer.get(), true, &timed_out);
+ Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out);
if (thread != NULL) {
jobject trace;
{
@@ -67,7 +55,7 @@ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint th
trace = thread->CreateInternalStackTrace(soa);
}
// Restart suspended thread.
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ thread_list->Resume(thread, false);
return Thread::InternalStackTraceToStackTraceElementArray(env, trace);
} else {
if (timed_out) {
@@ -115,7 +103,7 @@ static void ThreadStatsGetterCallback(Thread* t, void* context) {
GetTaskStats(t->GetTid(), &native_thread_state, &utime, &stime, &task_cpu);
std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context);
- JDWP::Append4BE(bytes, t->GetThinLockId());
+ JDWP::Append4BE(bytes, t->GetThreadId());
JDWP::Append1BE(bytes, Dbg::ToJdwpThreadStatus(t->GetState()));
JDWP::Append4BE(bytes, t->GetTid());
JDWP::Append4BE(bytes, utime);
diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h
new file mode 100644
index 0000000000..d941ec31f0
--- /dev/null
+++ b/runtime/native/scoped_fast_native_object_access.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 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_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
+#define ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
+
+#include "base/casts.h"
+#include "jni_internal.h"
+#include "thread-inl.h"
+#include "mirror/art_method.h"
+
+namespace art {
+
+// Variant of ScopedObjectAccess that does no runnable transitions. Should only be used by "fast"
+// JNI methods.
+class ScopedFastNativeObjectAccess {
+ public:
+ explicit ScopedFastNativeObjectAccess(JNIEnv* env)
+ LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
+ SHARED_LOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE
+ : env_(down_cast<JNIEnvExt*>(env)), self_(ThreadForEnv(env)) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+ }
+
+ ~ScopedFastNativeObjectAccess() UNLOCK_FUNCTION(Locks::mutator_lock_) ALWAYS_INLINE {
+ }
+
+ Thread* Self() const {
+ return self_;
+ }
+
+ JNIEnvExt* Env() const {
+ return env_;
+ }
+
+ template<typename T>
+ T Decode(jobject obj) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+ return down_cast<T>(Self()->DecodeJObject(obj));
+ }
+
+ mirror::ArtField* DecodeField(jfieldID fid) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+#ifdef MOVING_GARBAGE_COLLECTOR
+ // TODO: we should make these unique weak globals if Field instances can ever move.
+ UNIMPLEMENTED(WARNING);
+#endif
+ return reinterpret_cast<mirror::ArtField*>(fid);
+ }
+
+ /*
+ * Variant of ScopedObjectAccessUnched::AddLocalReference that without JNI work arounds
+ * or check JNI that should be being used by fast native methods.
+ */
+ template<typename T>
+ T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
+ // Don't work with raw objects in non-runnable states.
+ DCHECK_EQ(Self()->GetState(), kRunnable);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000);
+
+ IndirectReferenceTable& locals = Env()->locals;
+
+ uint32_t cookie = Env()->local_ref_cookie;
+ IndirectRef ref = locals.Add(cookie, obj);
+
+ return reinterpret_cast<T>(ref);
+ }
+
+ private:
+ JNIEnvExt* const env_;
+ Thread* const self_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_SCOPED_FAST_NATIVE_OBJECT_ACCESS_H_
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index eece81a9e8..2c6d2810b1 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -19,12 +19,12 @@
#include "jni_internal.h"
#include "mirror/object.h"
#include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
namespace art {
static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
@@ -34,7 +34,7 @@ static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj,
}
static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
volatile int64_t* address = reinterpret_cast<volatile int64_t*>(raw_addr);
@@ -44,7 +44,7 @@ static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj,
}
static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
@@ -60,97 +60,97 @@ static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaOb
}
static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField32(MemberOffset(offset), false);
}
static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField32(MemberOffset(offset), true);
}
static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField32(MemberOffset(offset), newValue, false);
}
static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField32(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
ANDROID_MEMBAR_STORE();
obj->SetField32(MemberOffset(offset), newValue, false);
}
static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField64(MemberOffset(offset), false);
}
static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
return obj->GetField64(MemberOffset(offset), true);
}
static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField64(MemberOffset(offset), newValue, false);
}
static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
obj->SetField64(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
ANDROID_MEMBAR_STORE();
obj->SetField64(MemberOffset(offset), newValue, false);
}
static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), true);
return soa.AddLocalReference<jobject>(value);
}
static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), false);
return soa.AddLocalReference<jobject>(value);
}
static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
obj->SetFieldObject(MemberOffset(offset), newValue, false);
}
static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
obj->SetFieldObject(MemberOffset(offset), newValue, true);
}
static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
- ScopedObjectAccess soa(env);
+ ScopedFastNativeObjectAccess soa(env);
mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
ANDROID_MEMBAR_STORE();
@@ -158,24 +158,24 @@ static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
- NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
- NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
- NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
- NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
- NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
- NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
- NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
- NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
- NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
- NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
- NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
- NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
- NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
+ NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
+ NATIVE_METHOD(Unsafe, compareAndSwapObject, "!(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(Unsafe, getIntVolatile, "!(Ljava/lang/Object;J)I"),
+ NATIVE_METHOD(Unsafe, putIntVolatile, "!(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Unsafe, getLongVolatile, "!(Ljava/lang/Object;J)J"),
+ NATIVE_METHOD(Unsafe, putLongVolatile, "!(Ljava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, getObjectVolatile, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ NATIVE_METHOD(Unsafe, putObjectVolatile, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, getInt, "!(Ljava/lang/Object;J)I"),
+ NATIVE_METHOD(Unsafe, putInt, "!(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Unsafe, putOrderedInt, "!(Ljava/lang/Object;JI)V"),
+ NATIVE_METHOD(Unsafe, getLong, "!(Ljava/lang/Object;J)J"),
+ NATIVE_METHOD(Unsafe, putLong, "!(Ljava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, putOrderedLong, "!(Ljava/lang/Object;JJ)V"),
+ NATIVE_METHOD(Unsafe, getObject, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ NATIVE_METHOD(Unsafe, putObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
};
void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index c01f77c364..94897950e0 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,13 +22,14 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '0', '7', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '1', '0', '\0' };
OatHeader::OatHeader() {
memset(this, 0, sizeof(*this));
}
OatHeader::OatHeader(InstructionSet instruction_set,
+ const InstructionSetFeatures& instruction_set_features,
const std::vector<const DexFile*>* dex_files,
uint32_t image_file_location_oat_checksum,
uint32_t image_file_location_oat_data_begin,
@@ -42,6 +43,9 @@ OatHeader::OatHeader(InstructionSet instruction_set,
instruction_set_ = instruction_set;
UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
+ instruction_set_features_ = instruction_set_features;
+ UpdateChecksum(&instruction_set_features_, sizeof(instruction_set_features_));
+
dex_file_count_ = dex_files->size();
UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
@@ -60,8 +64,10 @@ OatHeader::OatHeader(InstructionSet instruction_set,
interpreter_to_interpreter_bridge_offset_ = 0;
interpreter_to_compiled_code_bridge_offset_ = 0;
jni_dlsym_lookup_offset_ = 0;
+ portable_imt_conflict_trampoline_offset_ = 0;
portable_resolution_trampoline_offset_ = 0;
portable_to_interpreter_bridge_offset_ = 0;
+ quick_imt_conflict_trampoline_offset_ = 0;
quick_resolution_trampoline_offset_ = 0;
quick_to_interpreter_bridge_offset_ = 0;
}
@@ -97,6 +103,11 @@ InstructionSet OatHeader::GetInstructionSet() const {
return instruction_set_;
}
+const InstructionSetFeatures& OatHeader::GetInstructionSetFeatures() const {
+ CHECK(IsValid());
+ return instruction_set_features_;
+}
+
uint32_t OatHeader::GetExecutableOffset() const {
DCHECK(IsValid());
DCHECK_ALIGNED(executable_offset_, kPageSize);
@@ -171,18 +182,37 @@ void OatHeader::SetJniDlsymLookupOffset(uint32_t offset) {
UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(offset));
}
+const void* OatHeader::GetPortableImtConflictTrampoline() const {
+ return reinterpret_cast<const uint8_t*>(this) + GetPortableImtConflictTrampolineOffset();
+}
+
+uint32_t OatHeader::GetPortableImtConflictTrampolineOffset() const {
+ DCHECK(IsValid());
+ CHECK_GE(portable_imt_conflict_trampoline_offset_, jni_dlsym_lookup_offset_);
+ return portable_imt_conflict_trampoline_offset_;
+}
+
+void OatHeader::SetPortableImtConflictTrampolineOffset(uint32_t offset) {
+ CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
+ DCHECK(IsValid());
+ DCHECK_EQ(portable_imt_conflict_trampoline_offset_, 0U) << offset;
+
+ portable_imt_conflict_trampoline_offset_ = offset;
+ UpdateChecksum(&portable_imt_conflict_trampoline_offset_, sizeof(offset));
+}
+
const void* OatHeader::GetPortableResolutionTrampoline() const {
return reinterpret_cast<const uint8_t*>(this) + GetPortableResolutionTrampolineOffset();
}
uint32_t OatHeader::GetPortableResolutionTrampolineOffset() const {
DCHECK(IsValid());
- CHECK_GE(portable_resolution_trampoline_offset_, jni_dlsym_lookup_offset_);
+ CHECK_GE(portable_resolution_trampoline_offset_, portable_imt_conflict_trampoline_offset_);
return portable_resolution_trampoline_offset_;
}
void OatHeader::SetPortableResolutionTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= jni_dlsym_lookup_offset_);
+ CHECK(offset == 0 || offset >= portable_imt_conflict_trampoline_offset_);
DCHECK(IsValid());
DCHECK_EQ(portable_resolution_trampoline_offset_, 0U) << offset;
@@ -209,18 +239,37 @@ void OatHeader::SetPortableToInterpreterBridgeOffset(uint32_t offset) {
UpdateChecksum(&portable_to_interpreter_bridge_offset_, sizeof(offset));
}
+const void* OatHeader::GetQuickImtConflictTrampoline() const {
+ return reinterpret_cast<const uint8_t*>(this) + GetQuickImtConflictTrampolineOffset();
+}
+
+uint32_t OatHeader::GetQuickImtConflictTrampolineOffset() const {
+ DCHECK(IsValid());
+ CHECK_GE(quick_imt_conflict_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+ return quick_imt_conflict_trampoline_offset_;
+}
+
+void OatHeader::SetQuickImtConflictTrampolineOffset(uint32_t offset) {
+ CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+ DCHECK(IsValid());
+ DCHECK_EQ(quick_imt_conflict_trampoline_offset_, 0U) << offset;
+
+ quick_imt_conflict_trampoline_offset_ = offset;
+ UpdateChecksum(&quick_imt_conflict_trampoline_offset_, sizeof(offset));
+}
+
const void* OatHeader::GetQuickResolutionTrampoline() const {
return reinterpret_cast<const uint8_t*>(this) + GetQuickResolutionTrampolineOffset();
}
uint32_t OatHeader::GetQuickResolutionTrampolineOffset() const {
DCHECK(IsValid());
- CHECK_GE(quick_resolution_trampoline_offset_, portable_to_interpreter_bridge_offset_);
+ CHECK_GE(quick_resolution_trampoline_offset_, quick_imt_conflict_trampoline_offset_);
return quick_resolution_trampoline_offset_;
}
void OatHeader::SetQuickResolutionTrampolineOffset(uint32_t offset) {
- CHECK(offset == 0 || offset >= portable_to_interpreter_bridge_offset_);
+ CHECK(offset == 0 || offset >= quick_imt_conflict_trampoline_offset_);
DCHECK(IsValid());
DCHECK_EQ(quick_resolution_trampoline_offset_, 0U) << offset;
diff --git a/runtime/oat.h b/runtime/oat.h
index a653cf8d90..de840b5870 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,6 +32,7 @@ class PACKED(4) OatHeader {
OatHeader();
OatHeader(InstructionSet instruction_set,
+ const InstructionSetFeatures& instruction_set_features,
const std::vector<const DexFile*>* dex_files,
uint32_t image_file_location_oat_checksum,
uint32_t image_file_location_oat_data_begin,
@@ -62,6 +63,9 @@ class PACKED(4) OatHeader {
const void* GetPortableResolutionTrampoline() const;
uint32_t GetPortableResolutionTrampolineOffset() const;
void SetPortableResolutionTrampolineOffset(uint32_t offset);
+ const void* GetPortableImtConflictTrampoline() const;
+ uint32_t GetPortableImtConflictTrampolineOffset() const;
+ void SetPortableImtConflictTrampolineOffset(uint32_t offset);
const void* GetPortableToInterpreterBridge() const;
uint32_t GetPortableToInterpreterBridgeOffset() const;
void SetPortableToInterpreterBridgeOffset(uint32_t offset);
@@ -69,11 +73,15 @@ class PACKED(4) OatHeader {
const void* GetQuickResolutionTrampoline() const;
uint32_t GetQuickResolutionTrampolineOffset() const;
void SetQuickResolutionTrampolineOffset(uint32_t offset);
+ const void* GetQuickImtConflictTrampoline() const;
+ uint32_t GetQuickImtConflictTrampolineOffset() const;
+ void SetQuickImtConflictTrampolineOffset(uint32_t offset);
const void* GetQuickToInterpreterBridge() const;
uint32_t GetQuickToInterpreterBridgeOffset() const;
void SetQuickToInterpreterBridgeOffset(uint32_t offset);
InstructionSet GetInstructionSet() const;
+ const InstructionSetFeatures& GetInstructionSetFeatures() const;
uint32_t GetImageFileLocationOatChecksum() const;
uint32_t GetImageFileLocationOatDataBegin() const;
uint32_t GetImageFileLocationSize() const;
@@ -86,13 +94,16 @@ class PACKED(4) OatHeader {
uint32_t adler32_checksum_;
InstructionSet instruction_set_;
+ InstructionSetFeatures instruction_set_features_;
uint32_t dex_file_count_;
uint32_t executable_offset_;
uint32_t interpreter_to_interpreter_bridge_offset_;
uint32_t interpreter_to_compiled_code_bridge_offset_;
uint32_t jni_dlsym_lookup_offset_;
+ uint32_t portable_imt_conflict_trampoline_offset_;
uint32_t portable_resolution_trampoline_offset_;
uint32_t portable_to_interpreter_bridge_offset_;
+ uint32_t quick_imt_conflict_trampoline_offset_;
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
@@ -104,6 +115,19 @@ class PACKED(4) OatHeader {
DISALLOW_COPY_AND_ASSIGN(OatHeader);
};
+// OatMethodOffsets are currently 7x32-bits=224-bits long, so if we can
+// save even one OatMethodOffsets struct, the more complicated encoding
+// using a bitmap pays for itself since few classes will have 224
+// methods.
+enum OatClassType {
+ kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method.
+ kOatClassSomeCompiled = 1, // A bitmap of which OatMethodOffsets are present follows the OatClass.
+ kOatClassNoneCompiled = 2, // All methods are interpretted so no OatMethodOffsets are necessary.
+ kOatClassMax = 3,
+};
+
+std::ostream& operator<<(std::ostream& os, const OatClassType& rhs);
+
class PACKED(4) OatMethodOffsets {
public:
OatMethodOffsets();
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 7ecaf012ae..fa2b485822 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -18,6 +18,7 @@
#include <dlfcn.h>
+#include "base/bit_vector.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "elf_file.h"
@@ -48,19 +49,21 @@ void OatFile::CheckLocation(const std::string& location) {
}
OatFile* OatFile::OpenMemory(std::vector<uint8_t>& oat_contents,
- const std::string& location) {
+ const std::string& location,
+ std::string* error_msg) {
CHECK(!oat_contents.empty()) << location;
CheckLocation(location);
UniquePtr<OatFile> oat_file(new OatFile(location));
oat_file->begin_ = &oat_contents[0];
oat_file->end_ = &oat_contents[oat_contents.size()];
- return oat_file->Setup() ? oat_file.release() : NULL;
+ return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
}
OatFile* OatFile::Open(const std::string& filename,
const std::string& location,
byte* requested_base,
- bool executable) {
+ bool executable,
+ std::string* error_msg) {
CHECK(!filename.empty()) << location;
CheckLocation(filename);
#ifdef ART_USE_PORTABLE_COMPILER
@@ -70,7 +73,7 @@ OatFile* OatFile::Open(const std::string& filename,
// open a generated dex file by name, remove the file, then open
// another generated dex file with the same name. http://b/10614658
if (executable) {
- return OpenDlopen(filename, location, requested_base);
+ return OpenDlopen(filename, location, requested_base, error_msg);
}
#endif
// If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
@@ -83,21 +86,22 @@ OatFile* OatFile::Open(const std::string& filename,
if (file.get() == NULL) {
return NULL;
}
- return OpenElfFile(file.get(), location, requested_base, false, executable);
+ return OpenElfFile(file.get(), location, requested_base, false, executable, error_msg);
}
-OatFile* OatFile::OpenWritable(File* file, const std::string& location) {
+OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, true, false);
+ return OpenElfFile(file, location, NULL, true, false, error_msg);
}
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
const std::string& location,
- byte* requested_base) {
+ byte* requested_base,
+ std::string* error_msg) {
UniquePtr<OatFile> oat_file(new OatFile(location));
- bool success = oat_file->Dlopen(elf_filename, requested_base);
+ bool success = oat_file->Dlopen(elf_filename, requested_base, error_msg);
if (!success) {
- return NULL;
+ return nullptr;
}
return oat_file.release();
}
@@ -106,11 +110,13 @@ OatFile* OatFile::OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
bool writable,
- bool executable) {
+ bool executable,
+ std::string* error_msg) {
UniquePtr<OatFile> oat_file(new OatFile(location));
- bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable);
+ bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
if (!success) {
- return NULL;
+ CHECK(!error_msg->empty());
+ return nullptr;
}
return oat_file.release();
}
@@ -127,120 +133,117 @@ OatFile::~OatFile() {
}
}
-bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base) {
+bool OatFile::Dlopen(const std::string& elf_filename, byte* requested_base,
+ std::string* error_msg) {
char* absolute_path = realpath(elf_filename.c_str(), NULL);
if (absolute_path == NULL) {
- VLOG(class_linker) << "Failed to find absolute path for " << elf_filename;
+ *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str());
return false;
}
dlopen_handle_ = dlopen(absolute_path, RTLD_NOW);
free(absolute_path);
if (dlopen_handle_ == NULL) {
- VLOG(class_linker) << "Failed to dlopen " << elf_filename << ": " << dlerror();
+ *error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror());
return false;
}
begin_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatdata"));
if (begin_ == NULL) {
- LOG(WARNING) << "Failed to find oatdata symbol in " << elf_filename << ": " << dlerror();
+ *error_msg = StringPrintf("Failed to find oatdata symbol in '%s': %s", elf_filename.c_str(),
+ dlerror());
return false;
}
if (requested_base != NULL && begin_ != requested_base) {
- std::string maps;
- ReadFileToString("/proc/self/maps", &maps);
- LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata="
- << reinterpret_cast<const void*>(begin_) << " != expected="
- << reinterpret_cast<const void*>(requested_base)
- << " /proc/self/maps:\n" << maps;
+ *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
+ "oatdata=%p != expected=%p /proc/self/maps:\n",
+ begin_, requested_base);
+ ReadFileToString("/proc/self/maps", error_msg);
return false;
}
end_ = reinterpret_cast<byte*>(dlsym(dlopen_handle_, "oatlastword"));
if (end_ == NULL) {
- LOG(WARNING) << "Failed to find oatlastword symbol in " << elf_filename << ": " << dlerror();
+ *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s': %s", elf_filename.c_str(),
+ dlerror());
return false;
}
// Readjust to be non-inclusive upper bound.
end_ += sizeof(uint32_t);
- return Setup();
+ return Setup(error_msg);
}
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable) {
- elf_file_.reset(ElfFile::Open(file, writable, true));
- if (elf_file_.get() == NULL) {
- if (writable) {
- PLOG(WARNING) << "Failed to open ELF file for " << file->GetPath();
- }
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ std::string* error_msg) {
+ elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+ if (elf_file_.get() == nullptr) {
+ DCHECK(!error_msg->empty());
return false;
}
- bool loaded = elf_file_->Load(executable);
+ bool loaded = elf_file_->Load(executable, error_msg);
if (!loaded) {
- LOG(WARNING) << "Failed to load ELF file " << file->GetPath();
+ DCHECK(!error_msg->empty());
return false;
}
begin_ = elf_file_->FindDynamicSymbolAddress("oatdata");
if (begin_ == NULL) {
- LOG(WARNING) << "Failed to find oatdata symbol in " << file->GetPath();
+ *error_msg = StringPrintf("Failed to find oatdata symbol in '%s'", file->GetPath().c_str());
return false;
}
if (requested_base != NULL && begin_ != requested_base) {
- std::string maps;
- ReadFileToString("/proc/self/maps", &maps);
- LOG(WARNING) << "Failed to find oatdata symbol at expected address: oatdata="
- << reinterpret_cast<const void*>(begin_) << " != expected="
- << reinterpret_cast<const void*>(requested_base)
- << " /proc/self/maps:\n" << maps;
+ *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
+ "oatdata=%p != expected=%p /proc/self/maps:\n",
+ begin_, requested_base);
+ ReadFileToString("/proc/self/maps", error_msg);
return false;
}
end_ = elf_file_->FindDynamicSymbolAddress("oatlastword");
if (end_ == NULL) {
- LOG(WARNING) << "Failed to find oatlastword symbol in " << file->GetPath();
+ *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s'", file->GetPath().c_str());
return false;
}
// Readjust to be non-inclusive upper bound.
end_ += sizeof(uint32_t);
- return Setup();
+ return Setup(error_msg);
}
-bool OatFile::Setup() {
+bool OatFile::Setup(std::string* error_msg) {
if (!GetOatHeader().IsValid()) {
- LOG(WARNING) << "Invalid oat magic for " << GetLocation();
+ *error_msg = StringPrintf("Invalid oat magic for '%s'", GetLocation().c_str());
return false;
}
const byte* oat = Begin();
oat += sizeof(OatHeader);
if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found truncated OatHeader";
+ *error_msg = StringPrintf("In oat file '%s' found truncated OatHeader", GetLocation().c_str());
return false;
}
oat += GetOatHeader().GetImageFileLocationSize();
if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found truncated image file location: "
- << reinterpret_cast<const void*>(Begin())
- << "+" << sizeof(OatHeader)
- << "+" << GetOatHeader().GetImageFileLocationSize()
- << "<=" << reinterpret_cast<const void*>(End());
+ *error_msg = StringPrintf("In oat file '%s' found truncated image file location: "
+ "%p + %zd + %ud <= %p", GetLocation().c_str(),
+ Begin(), sizeof(OatHeader), GetOatHeader().GetImageFileLocationSize(),
+ End());
return false;
}
for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) {
size_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
- if (dex_file_location_size == 0U) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " with empty location name";
+ if (UNLIKELY(dex_file_location_size == 0U)) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with empty location name",
+ GetLocation().c_str(), i);
return false;
}
oat += sizeof(dex_file_location_size);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " truncated after dex file location size";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd truncated after dex file "
+ "location size", GetLocation().c_str(), i);
return false;
}
const char* dex_file_location_data = reinterpret_cast<const char*>(oat);
oat += dex_file_location_size;
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " with truncated dex file location";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with truncated dex file "
+ "location", GetLocation().c_str(), i);
return false;
}
@@ -248,55 +251,54 @@ bool OatFile::Setup() {
uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
oat += sizeof(dex_file_checksum);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " truncated after dex file checksum";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated after "
+ "dex file checksum", GetLocation().c_str(), i,
+ dex_file_location.c_str());
return false;
}
uint32_t dex_file_offset = *reinterpret_cast<const uint32_t*>(oat);
- if (dex_file_offset == 0U) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with zero dex file offset";
+ if (UNLIKELY(dex_file_offset == 0U)) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with zero dex "
+ "file offset", GetLocation().c_str(), i, dex_file_location.c_str());
return false;
}
- if (dex_file_offset > Size()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with dex file offset" << dex_file_offset << " > " << Size();
+ if (UNLIKELY(dex_file_offset > Size())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with dex file "
+ "offset %ud > %zd", GetLocation().c_str(), i,
+ dex_file_location.c_str(), dex_file_offset, Size());
return false;
}
oat += sizeof(dex_file_offset);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " truncated after dex file offset";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated "
+ " after dex file offsets", GetLocation().c_str(), i,
+ dex_file_location.c_str());
return false;
}
const uint8_t* dex_file_pointer = Begin() + dex_file_offset;
- if (!DexFile::IsMagicValid(dex_file_pointer)) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with invalid dex file magic: " << dex_file_pointer;
+ if (UNLIKELY(!DexFile::IsMagicValid(dex_file_pointer))) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
+ " dex file magic '%s'", GetLocation().c_str(), i,
+ dex_file_location.c_str(), dex_file_pointer);
return false;
}
- if (!DexFile::IsVersionValid(dex_file_pointer)) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with invalid dex file version: " << dex_file_pointer;
+ if (UNLIKELY(!DexFile::IsVersionValid(dex_file_pointer))) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with invalid "
+ " dex file version '%s'", GetLocation().c_str(), i,
+ dex_file_location.c_str(), dex_file_pointer);
return false;
}
const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_pointer);
const uint32_t* methods_offsets_pointer = reinterpret_cast<const uint32_t*>(oat);
oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_);
- if (oat > End()) {
- LOG(ERROR) << "In oat file " << GetLocation() << " found OatDexFile # " << i
- << " for "<< dex_file_location
- << " with truncated method offsets";
+ if (UNLIKELY(oat > End())) {
+ *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated "
+ " method offsets", GetLocation().c_str(), i,
+ dex_file_location.c_str());
return false;
}
@@ -323,8 +325,8 @@ const byte* OatFile::End() const {
return end_;
}
-const OatFile::OatDexFile* OatFile::GetOatDexFile(const std::string& dex_location,
- const uint32_t* const dex_location_checksum,
+const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
+ const uint32_t* dex_location_checksum,
bool warn_if_not_found) const {
Table::const_iterator it = oat_dex_files_.find(dex_location);
if (it != oat_dex_files_.end()) {
@@ -373,9 +375,9 @@ size_t OatFile::OatDexFile::FileSize() const {
return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_;
}
-const DexFile* OatFile::OatDexFile::OpenDexFile() const {
+const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_);
+ dex_file_location_checksum_, error_msg);
}
const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const {
@@ -383,29 +385,92 @@ const OatFile::OatClass* OatFile::OatDexFile::GetOatClass(uint16_t class_def_ind
const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset;
CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation();
- mirror::Class::Status status = *reinterpret_cast<const mirror::Class::Status*>(oat_class_pointer);
- const byte* methods_pointer = oat_class_pointer + sizeof(status);
+ const byte* status_pointer = oat_class_pointer;
+ CHECK_LT(status_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ mirror::Class::Status status =
+ static_cast<mirror::Class::Status>(*reinterpret_cast<const int16_t*>(status_pointer));
+ CHECK_LT(status, mirror::Class::kStatusMax);
+
+ const byte* type_pointer = status_pointer + sizeof(uint16_t);
+ CHECK_LT(type_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ OatClassType type = static_cast<OatClassType>(*reinterpret_cast<const uint16_t*>(type_pointer));
+ CHECK_LT(type, kOatClassMax);
+
+ const byte* bitmap_pointer = type_pointer + sizeof(int16_t);
+ CHECK_LT(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ uint32_t bitmap_size = 0;
+ if (type == kOatClassSomeCompiled) {
+ bitmap_size = static_cast<uint32_t>(*reinterpret_cast<const uint32_t*>(bitmap_pointer));
+ bitmap_pointer += sizeof(bitmap_size);
+ CHECK_LT(bitmap_pointer, oat_file_->End()) << oat_file_->GetLocation();
+ }
+
+ const byte* methods_pointer = bitmap_pointer + bitmap_size;
CHECK_LT(methods_pointer, oat_file_->End()) << oat_file_->GetLocation();
return new OatClass(oat_file_,
status,
+ type,
+ bitmap_size,
+ reinterpret_cast<const uint32_t*>(bitmap_pointer),
reinterpret_cast<const OatMethodOffsets*>(methods_pointer));
}
OatFile::OatClass::OatClass(const OatFile* oat_file,
mirror::Class::Status status,
+ OatClassType type,
+ uint32_t bitmap_size,
+ const uint32_t* bitmap_pointer,
const OatMethodOffsets* methods_pointer)
- : oat_file_(oat_file), status_(status), methods_pointer_(methods_pointer) {}
-
-OatFile::OatClass::~OatClass() {}
+ : oat_file_(oat_file), status_(status), type_(type),
+ bitmap_(NULL), methods_pointer_(methods_pointer) {
+ switch (type_) {
+ case kOatClassAllCompiled: {
+ CHECK_EQ(0U, bitmap_size);
+ break;
+ }
+ case kOatClassSomeCompiled: {
+ CHECK_NE(0U, bitmap_size);
+ bitmap_ = new BitVector(0, false, Allocator::GetNoopAllocator(), bitmap_size,
+ const_cast<uint32_t*>(bitmap_pointer));
+ break;
+ }
+ case kOatClassNoneCompiled: {
+ CHECK_EQ(0U, bitmap_size);
+ methods_pointer_ = NULL;
+ break;
+ }
+ case kOatClassMax: {
+ LOG(FATAL) << "Invalid OatClassType " << type_;
+ break;
+ }
+ }
+}
-mirror::Class::Status OatFile::OatClass::GetStatus() const {
- return status_;
+OatFile::OatClass::~OatClass() {
+ delete bitmap_;
}
const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const {
- const OatMethodOffsets& oat_method_offsets = methods_pointer_[method_index];
+ if (methods_pointer_ == NULL) {
+ CHECK_EQ(kOatClassNoneCompiled, type_);
+ return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ }
+ size_t methods_pointer_index;
+ if (bitmap_ == NULL) {
+ CHECK_EQ(kOatClassAllCompiled, type_);
+ methods_pointer_index = method_index;
+ } else {
+ CHECK_EQ(kOatClassSomeCompiled, type_);
+ if (!bitmap_->IsBitSet(method_index)) {
+ return OatMethod(NULL, 0, 0, 0, 0, 0, 0, 0);
+ }
+ size_t num_set_bits = bitmap_->NumSetBits(method_index);
+ CHECK_NE(0U, num_set_bits);
+ methods_pointer_index = num_set_bits - 1;
+ }
+ const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index];
return OatMethod(
oat_file_->Begin(),
oat_method_offsets.code_offset_,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 270976f030..887a9d1b63 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -29,6 +29,7 @@
namespace art {
+class BitVector;
class ElfFile;
class MemMap;
class OatMethodOffsets;
@@ -45,18 +46,20 @@ class OatFile {
static OatFile* Open(const std::string& filename,
const std::string& location,
byte* requested_base,
- bool executable);
+ bool executable,
+ std::string* error_msg);
// Open an oat file from an already opened File.
// Does not use dlopen underneath so cannot be used for runtime use
// where relocations may be required. Currently used from
// ImageWriter which wants to open a writable version from an existing
// file descriptor for patching.
- static OatFile* OpenWritable(File* file, const std::string& location);
+ static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
// Open an oat file backed by a std::vector with the given location.
static OatFile* OpenMemory(std::vector<uint8_t>& oat_contents,
- const std::string& location);
+ const std::string& location,
+ std::string* error_msg);
~OatFile();
@@ -143,7 +146,13 @@ class OatFile {
class OatClass {
public:
- mirror::Class::Status GetStatus() const;
+ mirror::Class::Status GetStatus() const {
+ return status_;
+ }
+
+ OatClassType GetType() const {
+ return type_;
+ }
// get the OatMethod entry based on its index into the class
// defintion. direct methods come first, followed by virtual
@@ -155,10 +164,21 @@ class OatFile {
private:
OatClass(const OatFile* oat_file,
mirror::Class::Status status,
+ OatClassType type,
+ uint32_t bitmap_size,
+ const uint32_t* bitmap_pointer,
const OatMethodOffsets* methods_pointer);
const OatFile* oat_file_;
+
const mirror::Class::Status status_;
+ COMPILE_ASSERT(mirror::Class::Status::kStatusMax < (2 ^ 16), class_status_wont_fit_in_16bits);
+
+ OatClassType type_;
+ COMPILE_ASSERT(OatClassType::kOatClassMax < (2 ^ 16), oat_class_type_wont_fit_in_16bits);
+
+ const BitVector* bitmap_;
+
const OatMethodOffsets* methods_pointer_;
friend class OatDexFile;
@@ -167,7 +187,7 @@ class OatFile {
class OatDexFile {
public:
// Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
- const DexFile* OpenDexFile() const;
+ const DexFile* OpenDexFile(std::string* error_msg) const;
// Returns the size of the DexFile refered to by this OatDexFile.
size_t FileSize() const;
@@ -204,10 +224,10 @@ class OatFile {
DISALLOW_COPY_AND_ASSIGN(OatDexFile);
};
- const OatDexFile* GetOatDexFile(const std::string& dex_location,
+ const OatDexFile* GetOatDexFile(const char* dex_location,
const uint32_t* const dex_location_checksum,
- bool exception_if_not_found = true) const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool exception_if_not_found = true) const;
+
std::vector<const OatDexFile*> GetOatDexFiles() const;
size_t Size() const {
@@ -219,18 +239,21 @@ class OatFile {
static OatFile* OpenDlopen(const std::string& elf_filename,
const std::string& location,
- byte* requested_base);
+ byte* requested_base,
+ std::string* error_msg);
static OatFile* OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
bool writable,
- bool executable);
+ bool executable,
+ std::string* error_msg);
explicit OatFile(const std::string& filename);
- bool Dlopen(const std::string& elf_filename, byte* requested_base);
- bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable);
- bool Setup();
+ bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
+ bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ std::string* error_msg);
+ bool Setup(std::string* error_msg);
const byte* Begin() const;
const byte* End() const;
diff --git a/runtime/object_utils.h b/runtime/object_utils.h
index 6ee3016179..f724776f2e 100644
--- a/runtime/object_utils.h
+++ b/runtime/object_utils.h
@@ -36,7 +36,8 @@ namespace art {
class ObjectLock {
public:
- explicit ObjectLock(Thread* self, mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ explicit ObjectLock(Thread* self, mirror::Object* object)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
: self_(self), obj_(object) {
CHECK(object != NULL);
obj_->MonitorEnter(self_);
@@ -267,53 +268,56 @@ class FieldHelper {
}
field_ = new_f;
}
+
const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- return dex_file.GetFieldName(dex_file.GetFieldId(field_index));
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
DCHECK(field_->IsStatic());
DCHECK_LT(field_index, 2U);
return field_index == 0 ? "interfaces" : "throws";
}
+ const DexFile& dex_file = GetDexFile();
+ return dex_file.GetFieldName(dex_file.GetFieldId(field_index));
}
+
mirror::Class* GetType(bool resolve = true) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_);
- if (resolve && (type == NULL)) {
- type = GetClassLinker()->ResolveType(field_id.type_idx_, field_);
- CHECK(type != NULL || Thread::Current()->IsExceptionPending());
- }
- return type;
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
return GetClassLinker()->FindSystemClass(GetTypeDescriptor());
}
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_);
+ if (resolve && (type == NULL)) {
+ type = GetClassLinker()->ResolveType(field_id.type_idx_, field_);
+ CHECK(type != NULL || Thread::Current()->IsExceptionPending());
+ }
+ return type;
}
+
const char* GetTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- return dex_file.GetFieldTypeDescriptor(field_id);
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
DCHECK(field_->IsStatic());
DCHECK_LT(field_index, 2U);
// 0 == Class[] interfaces; 1 == Class[][] throws;
return field_index == 0 ? "[Ljava/lang/Class;" : "[[Ljava/lang/Class;";
}
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ return dex_file.GetFieldTypeDescriptor(field_id);
}
+
Primitive::Type GetTypeAsPrimitiveType()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return Primitive::GetType(GetTypeDescriptor()[0]);
}
+
bool IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Primitive::Type type = GetTypeAsPrimitiveType();
return type != Primitive::kPrimNot;
}
+
size_t FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Primitive::Type type = GetTypeAsPrimitiveType();
return Primitive::FieldSize(type);
@@ -324,11 +328,7 @@ class FieldHelper {
const char* GetDeclaringClassDescriptor()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
uint32_t field_index = field_->GetDexFieldIndex();
- if (!field_->GetDeclaringClass()->IsProxyClass()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
- return dex_file.GetFieldDeclaringClassDescriptor(field_id);
- } else {
+ if (UNLIKELY(field_->GetDeclaringClass()->IsProxyClass())) {
DCHECK(field_->IsStatic());
DCHECK_LT(field_index, 2U);
// 0 == Class[] interfaces; 1 == Class[][] throws;
@@ -336,6 +336,9 @@ class FieldHelper {
declaring_class_descriptor_ = kh.GetDescriptor();
return declaring_class_descriptor_.c_str();
}
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
+ return dex_file.GetFieldDeclaringClassDescriptor(field_id);
}
private:
@@ -417,12 +420,14 @@ class MethodHelper {
const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
- if (dex_method_idx != DexFile::kDexNoIndex) {
+ if (LIKELY(dex_method_idx != DexFile::kDexNoIndex)) {
return dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
} else {
Runtime* runtime = Runtime::Current();
if (method_ == runtime->GetResolutionMethod()) {
return "<runtime internal resolution method>";
+ } else if (method_ == runtime->GetImtConflictMethod()) {
+ return "<runtime internal imt conflict method>";
} else if (method_ == runtime->GetCalleeSaveMethod(Runtime::kSaveAll)) {
return "<runtime internal callee-save all registers method>";
} else if (method_ == runtime->GetCalleeSaveMethod(Runtime::kRefsOnly)) {
@@ -460,13 +465,13 @@ class MethodHelper {
return shorty_len_;
}
- const std::string GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const Signature GetSignature() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
if (dex_method_idx != DexFile::kDexNoIndex) {
return dex_file.GetMethodSignature(dex_file.GetMethodId(dex_method_idx));
} else {
- return "<no signature>";
+ return Signature::NoSignature();
}
}
@@ -508,11 +513,10 @@ class MethodHelper {
const char* GetDeclaringClassDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile& dex_file = GetDexFile();
uint32_t dex_method_idx = method_->GetDexMethodIndex();
- if (dex_method_idx != DexFile::kDexNoIndex) {
- return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx));
- } else {
+ if (UNLIKELY(dex_method_idx == DexFile::kDexNoIndex)) {
return "<runtime method>";
}
+ return dex_file.GetMethodDeclaringClassDescriptor(dex_file.GetMethodId(dex_method_idx));
}
const char* GetDeclaringClassSourceFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -536,7 +540,7 @@ class MethodHelper {
}
bool IsClassInitializer() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return IsStatic() && StringPiece(GetName()) == "<clinit>";
+ return method_->IsConstructor() && IsStatic();
}
size_t NumArgs() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -569,16 +573,21 @@ class MethodHelper {
bool HasSameNameAndSignature(MethodHelper* other)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DexFile& dex_file = GetDexFile();
+ const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex());
if (GetDexCache() == other->GetDexCache()) {
- const DexFile& dex_file = GetDexFile();
- const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex());
const DexFile::MethodId& other_mid =
dex_file.GetMethodId(other->method_->GetDexMethodIndex());
return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_;
}
- StringPiece name(GetName());
- StringPiece other_name(other->GetName());
- return name == other_name && GetSignature() == other->GetSignature();
+ const DexFile& other_dex_file = other->GetDexFile();
+ const DexFile::MethodId& other_mid =
+ other_dex_file.GetMethodId(other->method_->GetDexMethodIndex());
+ if (!DexFileStringEquals(&dex_file, mid.name_idx_,
+ &other_dex_file, other_mid.name_idx_)) {
+ return false; // Name mismatch.
+ }
+ return dex_file.GetMethodSignature(mid) == other_dex_file.GetMethodSignature(other_mid);
}
const DexFile::CodeItem* GetCodeItem()
@@ -639,6 +648,46 @@ class MethodHelper {
return s;
}
+ uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const DexFile& dexfile = GetDexFile();
+ if (&dexfile == &other_dexfile) {
+ return method_->GetDexMethodIndex();
+ }
+ const DexFile::MethodId& mid = dexfile.GetMethodId(method_->GetDexMethodIndex());
+ 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 char* mid_name = dexfile.GetMethodName(mid);
+ const DexFile::StringId* other_name = other_dexfile.FindStringId(mid_name);
+ if (other_name != nullptr) {
+ uint16_t other_return_type_idx;
+ std::vector<uint16_t> other_param_type_idxs;
+ bool success = other_dexfile.CreateTypeList(dexfile.GetMethodSignature(mid).ToString(),
+ &other_return_type_idx,
+ &other_param_type_idxs);
+ if (success) {
+ const DexFile::ProtoId* other_sig =
+ other_dexfile.FindProtoId(other_return_type_idx, other_param_type_idxs);
+ if (other_sig != nullptr) {
+ const DexFile::MethodId* other_mid = other_dexfile.FindMethodId(*other_type_id,
+ *other_name,
+ *other_sig);
+ if (other_mid != nullptr) {
+ return other_dexfile.GetIndexForMethodId(*other_mid);
+ }
+ }
+ }
+ }
+ }
+ }
+ return DexFile::kDexNoIndex;
+ }
+
private:
// Set the method_ field, for proxy methods looking up the interface method via the resolved
// methods table.
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 8e23cbb153..e95fdb9226 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -38,16 +38,16 @@ ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max
ReferenceTable::~ReferenceTable() {
}
-void ReferenceTable::Add(const mirror::Object* obj) {
+void ReferenceTable::Add(mirror::Object* obj) {
DCHECK(obj != NULL);
- if (entries_.size() == max_size_) {
+ if (entries_.size() >= max_size_) {
LOG(FATAL) << "ReferenceTable '" << name_ << "' "
<< "overflowed (" << max_size_ << " entries)";
}
entries_.push_back(obj);
}
-void ReferenceTable::Remove(const mirror::Object* obj) {
+void ReferenceTable::Remove(mirror::Object* obj) {
// We iterate backwards on the assumption that references are LIFO.
for (int i = entries_.size() - 1; i >= 0; --i) {
if (entries_[i] == obj) {
@@ -232,8 +232,8 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) {
}
void ReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) {
- for (const auto& ref : entries_) {
- visitor(ref, arg);
+ for (auto& ref : entries_) {
+ ref = visitor(const_cast<mirror::Object*>(ref), arg);
}
}
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index e369fd0de5..37b31723ae 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -39,9 +39,9 @@ class ReferenceTable {
ReferenceTable(const char* name, size_t initial_size, size_t max_size);
~ReferenceTable();
- void Add(const mirror::Object* obj);
+ void Add(mirror::Object* obj);
- void Remove(const mirror::Object* obj);
+ void Remove(mirror::Object* obj);
size_t Size() const;
@@ -50,7 +50,7 @@ class ReferenceTable {
void VisitRoots(RootVisitor* visitor, void* arg);
private:
- typedef std::vector<const mirror::Object*> Table;
+ typedef std::vector<mirror::Object*> Table;
static void Dump(std::ostream& os, const Table& entries)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
friend class IndirectReferenceTable; // For Dump.
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 3e58b4bd94..80e16aa6c0 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -323,7 +323,7 @@ static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object*
}
JValue boxed_value;
- std::string src_descriptor(ClassHelper(o->GetClass()).GetDescriptor());
+ const StringPiece src_descriptor(ClassHelper(o->GetClass()).GetDescriptor());
mirror::Class* src_class = NULL;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
mirror::ArtField* primitive_field = o->GetClass()->GetIFields()->Get(0);
@@ -356,7 +356,7 @@ static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object*
StringPrintf("%s has type %s, got %s",
UnboxingFailureKind(m, index, f).c_str(),
PrettyDescriptor(dst_class).c_str(),
- PrettyDescriptor(src_descriptor.c_str()).c_str()).c_str());
+ PrettyDescriptor(src_descriptor.data()).c_str()).c_str());
return false;
}
diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h
index 3aa9b4bac0..a2d898b43c 100644
--- a/runtime/root_visitor.h
+++ b/runtime/root_visitor.h
@@ -23,7 +23,8 @@ class Object;
} // namespace mirror
class StackVisitor;
-typedef void (RootVisitor)(const mirror::Object* root, void* arg);
+typedef mirror::Object* (RootVisitor)(mirror::Object* root, void* arg)
+ __attribute__((warn_unused_result));
typedef void (VerifyRootVisitor)(const mirror::Object* root, void* arg, size_t vreg,
const StackVisitor* visitor);
typedef bool (IsMarkedTester)(const mirror::Object* object, void* arg);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index b9f54e5836..34cf45b4c7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -75,6 +75,7 @@ Runtime::Runtime()
is_explicit_gc_disabled_(false),
default_stack_size_(0),
heap_(NULL),
+ max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
monitor_list_(NULL),
thread_list_(NULL),
intern_table_(NULL),
@@ -83,6 +84,8 @@ Runtime::Runtime()
java_vm_(NULL),
pre_allocated_OutOfMemoryError_(NULL),
resolution_method_(NULL),
+ imt_conflict_method_(NULL),
+ default_imt_(NULL),
threads_being_born_(0),
shutdown_cond_(new ConditionVariable("Runtime shutdown", *Locks::runtime_shutdown_lock_)),
shutting_down_(false),
@@ -99,7 +102,8 @@ Runtime::Runtime()
use_compile_time_class_path_(false),
main_thread_group_(NULL),
system_thread_group_(NULL),
- system_class_loader_(NULL) {
+ system_class_loader_(NULL),
+ quick_alloc_entry_points_instrumentation_counter_(0) {
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
callee_save_methods_[i] = NULL;
}
@@ -319,6 +323,12 @@ size_t ParseIntegerOrDie(const std::string& s) {
return result;
}
+void Runtime::SweepSystemWeaks(RootVisitor* visitor, void* arg) {
+ GetInternTable()->SweepInternTableWeaks(visitor, arg);
+ GetMonitorList()->SweepMonitorList(visitor, arg);
+ GetJavaVM()->SweepJniWeakGlobals(visitor, arg);
+}
+
Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, bool ignore_unrecognized) {
UniquePtr<ParsedOptions> parsed(new ParsedOptions());
const char* boot_class_path_string = getenv("BOOTCLASSPATH");
@@ -343,6 +353,7 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b
// Only the main GC thread, no workers.
parsed->conc_gc_threads_ = 0;
parsed->stack_size_ = 0; // 0 means default.
+ parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
parsed->low_memory_mode_ = false;
parsed->is_compiler_ = false;
@@ -503,6 +514,10 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b
return NULL;
}
parsed->stack_size_ = size;
+ } else if (StartsWith(option, "-XX:MaxSpinsBeforeThinLockInflation=")) {
+ parsed->max_spins_before_thin_lock_inflation_ =
+ strtoul(option.substr(strlen("-XX:MaxSpinsBeforeThinLockInflation=")).c_str(),
+ nullptr, 10);
} else if (option == "-XX:LongPauseLogThreshold") {
parsed->long_pause_log_threshold_ =
ParseMemoryOption(option.substr(strlen("-XX:LongPauseLogThreshold=")).c_str(), 1024);
@@ -865,6 +880,8 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
default_stack_size_ = options->stack_size_;
stack_trace_file_ = options->stack_trace_file_;
+ max_spins_before_thin_lock_inflation_ = options->max_spins_before_thin_lock_inflation_;
+
monitor_list_ = new MonitorList;
thread_list_ = new ThreadList;
intern_table_ = new InternTable;
@@ -900,7 +917,7 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
// objects. We can't supply a thread group yet; it will be fixed later. Since we are the main
// thread, we do not get a java peer.
Thread* self = Thread::Attach("main", false, NULL, false);
- CHECK_EQ(self->thin_lock_id_, ThreadList::kMainId);
+ CHECK_EQ(self->thin_lock_thread_id_, ThreadList::kMainThreadId);
CHECK(self != NULL);
// Set us to runnable so tools using a runtime can allocate and GC by default
@@ -962,7 +979,7 @@ void Runtime::InitNativeMethods() {
std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
std::string reason;
self->TransitionFromSuspendedToRunnable();
- if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, reason)) {
+ if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, &reason)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
}
self->TransitionFromRunnableToSuspended(kNative);
@@ -1060,6 +1077,9 @@ void Runtime::SetStatsEnabled(bool new_state) {
GetStats()->Clear(~0);
// TODO: wouldn't it make more sense to clear _all_ threads' stats?
Thread::Current()->GetStats()->Clear(~0);
+ InstrumentQuickAllocEntryPoints();
+ } else {
+ UninstrumentQuickAllocEntryPoints();
}
stats_enabled_ = new_state;
}
@@ -1150,12 +1170,21 @@ void Runtime::VisitConcurrentRoots(RootVisitor* visitor, void* arg, bool only_di
void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) {
java_vm_->VisitRoots(visitor, arg);
- if (pre_allocated_OutOfMemoryError_ != NULL) {
- visitor(pre_allocated_OutOfMemoryError_, arg);
+ if (pre_allocated_OutOfMemoryError_ != nullptr) {
+ pre_allocated_OutOfMemoryError_ = reinterpret_cast<mirror::Throwable*>(
+ visitor(pre_allocated_OutOfMemoryError_, arg));
+ DCHECK(pre_allocated_OutOfMemoryError_ != nullptr);
}
- visitor(resolution_method_, arg);
+ resolution_method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(resolution_method_, arg));
+ DCHECK(resolution_method_ != nullptr);
+ imt_conflict_method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(imt_conflict_method_, arg));
+ DCHECK(imt_conflict_method_ != nullptr);
+ default_imt_ = reinterpret_cast<mirror::ObjectArray<mirror::ArtMethod>*>(visitor(default_imt_, arg));
+ DCHECK(default_imt_ != nullptr);
for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
- visitor(callee_save_methods_[i], arg);
+ callee_save_methods_[i] = reinterpret_cast<mirror::ArtMethod*>(
+ visitor(callee_save_methods_[i], arg));
+ DCHECK(callee_save_methods_[i] != nullptr);
}
}
@@ -1169,6 +1198,31 @@ void Runtime::VisitRoots(RootVisitor* visitor, void* arg, bool only_dirty, bool
VisitNonConcurrentRoots(visitor, arg);
}
+mirror::ObjectArray<mirror::ArtMethod>* Runtime::CreateDefaultImt(ClassLinker* cl) {
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, cl->AllocArtMethodArray(self, 64));
+ mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod();
+ for (size_t i = 0; i < 64; i++) {
+ imtable->Set(i, imt_conflict_method);
+ }
+ return imtable.get();
+}
+
+mirror::ArtMethod* Runtime::CreateImtConflictMethod() {
+ mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod();
+ Thread* self = Thread::Current();
+ SirtRef<mirror::ArtMethod>
+ method(self, down_cast<mirror::ArtMethod*>(method_class->AllocObject(self)));
+ method->SetDeclaringClass(method_class);
+ // TODO: use a special method for imt conflict method saves
+ method->SetDexMethodIndex(DexFile::kDexNoIndex);
+ // When compiling, the code pointer will get set later when the image is loaded.
+ Runtime* r = Runtime::Current();
+ ClassLinker* cl = r->GetClassLinker();
+ method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetImtConflictTrampoline(cl));
+ return method.get();
+}
+
mirror::ArtMethod* Runtime::CreateResolutionMethod() {
mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod();
Thread* self = Thread::Current();
@@ -1282,4 +1336,46 @@ void Runtime::SetCompileTimeClassPath(jobject class_loader, std::vector<const De
compile_time_class_paths_.Put(class_loader, class_path);
}
+static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) {
+ thread->ResetQuickAllocEntryPointsForThread();
+}
+
+void SetQuickAllocEntryPointsInstrumented(bool instrumented);
+
+void Runtime::InstrumentQuickAllocEntryPoints() {
+ ThreadList* tl = thread_list_;
+ Thread* self = Thread::Current();
+ tl->SuspendAll();
+ {
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_, 0);
+ int old_counter = quick_alloc_entry_points_instrumentation_counter_++;
+ if (old_counter == 0) {
+ // If it was disabled, enable it.
+ SetQuickAllocEntryPointsInstrumented(true);
+ tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL);
+ }
+ }
+ tl->ResumeAll();
+}
+
+void Runtime::UninstrumentQuickAllocEntryPoints() {
+ ThreadList* tl = thread_list_;
+ Thread* self = Thread::Current();
+ tl->SuspendAll();
+ {
+ MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(self, *Locks::thread_list_lock_);
+ DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_, 0);
+ int new_counter = --quick_alloc_entry_points_instrumentation_counter_;
+ if (new_counter == 0) {
+ // Disable it if the counter becomes zero.
+ SetQuickAllocEntryPointsInstrumented(false);
+ tl->ForEach(ResetQuickAllocEntryPointsForThread, NULL);
+ }
+ }
+ tl->ResumeAll();
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 7c2847949a..0ce2642c98 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -45,6 +45,7 @@ namespace gc {
namespace mirror {
class ArtMethod;
class ClassLoader;
+ template<class T> class ObjectArray;
template<class T> class PrimitiveArray;
typedef PrimitiveArray<int8_t> ByteArray;
class String;
@@ -112,6 +113,7 @@ class Runtime {
size_t parallel_gc_threads_;
size_t conc_gc_threads_;
size_t stack_size_;
+ size_t max_spins_before_thin_lock_inflation_;
bool low_memory_mode_;
size_t lock_profiling_threshold_;
std::string stack_trace_file_;
@@ -287,6 +289,10 @@ class Runtime {
return java_vm_;
}
+ size_t GetMaxSpinsBeforeThinkLockInflation() const {
+ return max_spins_before_thin_lock_inflation_;
+ }
+
MonitorList* GetMonitorList() const {
return monitor_list_;
}
@@ -324,6 +330,11 @@ class Runtime {
void VisitNonConcurrentRoots(RootVisitor* visitor, void* arg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Sweep system weaks, the system weak is deleted if the visitor return nullptr. Otherwise, the
+ // system weak is updated to be the visitor's returned value.
+ void SweepSystemWeaks(RootVisitor* visitor, void* arg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Returns a special method that calls into a trampoline for runtime method resolution
mirror::ArtMethod* GetResolutionMethod() const {
CHECK(HasResolutionMethod());
@@ -340,6 +351,39 @@ class Runtime {
mirror::ArtMethod* CreateResolutionMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns a special method that calls into a trampoline for runtime imt conflicts
+ mirror::ArtMethod* GetImtConflictMethod() const {
+ CHECK(HasImtConflictMethod());
+ return imt_conflict_method_;
+ }
+
+ bool HasImtConflictMethod() const {
+ return imt_conflict_method_ != NULL;
+ }
+
+ void SetImtConflictMethod(mirror::ArtMethod* method) {
+ imt_conflict_method_ = method;
+ }
+
+ mirror::ArtMethod* CreateImtConflictMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Returns an imt with every entry set to conflict, used as default imt for all classes.
+ mirror::ObjectArray<mirror::ArtMethod>* GetDefaultImt() const {
+ CHECK(HasDefaultImt());
+ return default_imt_;
+ }
+
+ bool HasDefaultImt() const {
+ return default_imt_ != NULL;
+ }
+
+ void SetDefaultImt(mirror::ObjectArray<mirror::ArtMethod>* imt) {
+ default_imt_ = imt;
+ }
+
+ mirror::ObjectArray<mirror::ArtMethod>* CreateDefaultImt(ClassLinker* cl)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Returns a special method that describes all callee saves being spilled to the stack.
enum CalleeSaveType {
kSaveAll,
@@ -398,6 +442,9 @@ class Runtime {
const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader);
void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path);
+ void InstrumentQuickAllocEntryPoints();
+ void UninstrumentQuickAllocEntryPoints();
+
private:
static void InitPlatformSignalHandlers();
@@ -452,6 +499,8 @@ class Runtime {
gc::Heap* heap_;
+ // The number of spins that are done before thread suspension is used to forcibly inflate.
+ size_t max_spins_before_thin_lock_inflation_;
MonitorList* monitor_list_;
ThreadList* thread_list_;
@@ -471,6 +520,10 @@ class Runtime {
mirror::ArtMethod* resolution_method_;
+ mirror::ArtMethod* imt_conflict_method_;
+
+ mirror::ObjectArray<mirror::ArtMethod>* default_imt_;
+
// A non-zero value indicates that a thread has been created but not yet initialized. Guarded by
// the shutdown lock so that threads aren't born while we're shutting down.
size_t threads_being_born_ GUARDED_BY(Locks::runtime_shutdown_lock_);
@@ -514,6 +567,8 @@ class Runtime {
// As returned by ClassLoader.getSystemClassLoader().
jobject system_class_loader_;
+ int quick_alloc_entry_points_instrumentation_counter_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index d3f3a88d66..c39cdb2679 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -18,7 +18,6 @@
#define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
#include "base/casts.h"
-#include "jni_internal.h"
#include "thread-inl.h"
namespace art {
@@ -122,14 +121,14 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange {
explicit ScopedObjectAccessUnchecked(JNIEnv* env)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) ALWAYS_INLINE
: ScopedThreadStateChange(ThreadForEnv(env), kRunnable),
- env_(reinterpret_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
+ env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
self_->VerifyStack();
}
explicit ScopedObjectAccessUnchecked(Thread* self)
LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
: ScopedThreadStateChange(self, kRunnable),
- env_(reinterpret_cast<JNIEnvExt*>(self->GetJniEnv())),
+ env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
vm_(env_ != NULL ? env_->vm : NULL) {
self_->VerifyStack();
}
@@ -137,7 +136,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange {
// Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
// change into Runnable or acquire a share on the mutator_lock_.
explicit ScopedObjectAccessUnchecked(JavaVM* vm)
- : ScopedThreadStateChange(), env_(NULL), vm_(reinterpret_cast<JavaVMExt*>(vm)) {}
+ : ScopedThreadStateChange(), env_(NULL), vm_(down_cast<JavaVMExt*>(vm)) {}
// Here purely to force inlining.
~ScopedObjectAccessUnchecked() ALWAYS_INLINE {
@@ -162,6 +161,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange {
*/
template<typename T>
T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertSharedHeld(Self());
DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states.
if (obj == NULL) {
return NULL;
@@ -245,11 +245,6 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange {
}
private:
- static Thread* ThreadForEnv(JNIEnv* env) {
- JNIEnvExt* full_env(reinterpret_cast<JNIEnvExt*>(env));
- return full_env->self;
- }
-
// The full JNIEnv.
JNIEnvExt* const env_;
// The full JavaVM.
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index 15eb27deb2..fe62e25443 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -147,7 +147,6 @@ void SignalCatcher::HandleSigQuit() {
CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable);
if (self->ReadFlag(kCheckpointRequest)) {
self->RunCheckpointFunction();
- self->AtomicClearFlag(kCheckpointRequest);
}
self->EndAssertNoThreadSuspension(old_cause);
thread_list->ResumeAll();
diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h
index 81f0dff217..a1f8a6693f 100644
--- a/runtime/sirt_ref.h
+++ b/runtime/sirt_ref.h
@@ -30,7 +30,8 @@ class SirtRef {
self_->PushSirt(&sirt_);
}
~SirtRef() {
- CHECK(self_->PopSirt() == &sirt_);
+ StackIndirectReferenceTable* top_sirt = self_->PopSirt();
+ DCHECK_EQ(top_sirt, &sirt_);
}
T& operator*() const { return *get(); }
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 206bff3425..5d3a9a5234 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -148,8 +148,8 @@ uint32_t StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kin
const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
DCHECK(code_item != NULL) << PrettyMethod(m); // Can't be NULL or how would we compile its instructions?
size_t frame_size = m->GetFrameSizeInBytes();
- return GetVReg(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(),
- frame_size, vreg);
+ return *GetVRegAddr(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(),
+ frame_size, vreg);
}
} else {
return cur_shadow_frame_->GetVReg(vreg);
@@ -253,7 +253,7 @@ std::string StackVisitor::DescribeLocation() const {
return result;
}
-instrumentation::InstrumentationStackFrame StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const {
+instrumentation::InstrumentationStackFrame& StackVisitor::GetInstrumentationStackFrame(uint32_t depth) const {
return thread_->GetInstrumentationStack()->at(depth);
}
@@ -309,7 +309,7 @@ void StackVisitor::WalkStack(bool include_transitions) {
// While profiling, the return pc is restored from the side stack, except when walking
// the stack for an exception where the side stack will be unwound in VisitFrame.
if (GetQuickInstrumentationExitPc() == return_pc) {
- instrumentation::InstrumentationStackFrame instrumentation_frame =
+ const instrumentation::InstrumentationStackFrame& instrumentation_frame =
GetInstrumentationStackFrame(instrumentation_stack_depth);
instrumentation_stack_depth++;
if (GetMethod() == Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveAll)) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 7c87f4555c..a4b93bc407 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -68,8 +68,7 @@ class ShadowFrame {
static ShadowFrame* Create(uint32_t num_vregs, ShadowFrame* link,
mirror::ArtMethod* method, uint32_t dex_pc) {
uint8_t* memory = new uint8_t[ComputeSize(num_vregs)];
- ShadowFrame* sf = new (memory) ShadowFrame(num_vregs, link, method, dex_pc, true);
- return sf;
+ return Create(num_vregs, link, method, dex_pc, memory);
}
// Create ShadowFrame for interpreter using provided memory.
@@ -154,7 +153,12 @@ class ShadowFrame {
mirror::Object* GetVRegReference(size_t i) const {
DCHECK_LT(i, NumberOfVRegs());
if (HasReferenceArray()) {
- return References()[i];
+ mirror::Object* ref = References()[i];
+ // If the vreg reference is not equal to the vreg then the vreg reference is stale.
+ if (reinterpret_cast<uint32_t>(ref) != vregs_[i]) {
+ return nullptr;
+ }
+ return ref;
} else {
const uint32_t* vreg = &vregs_[i];
return *reinterpret_cast<mirror::Object* const*>(vreg);
@@ -467,13 +471,14 @@ class StackVisitor {
uintptr_t GetGPR(uint32_t reg) const;
void SetGPR(uint32_t reg, uintptr_t value);
- uint32_t GetVReg(mirror::ArtMethod** cur_quick_frame, const DexFile::CodeItem* code_item,
+ // This is a fast-path for getting/setting values in a quick frame.
+ uint32_t* GetVRegAddr(mirror::ArtMethod** cur_quick_frame, const DexFile::CodeItem* code_item,
uint32_t core_spills, uint32_t fp_spills, size_t frame_size,
uint16_t vreg) const {
int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
DCHECK_EQ(cur_quick_frame, GetCurrentQuickFrame());
byte* vreg_addr = reinterpret_cast<byte*>(cur_quick_frame) + offset;
- return *reinterpret_cast<uint32_t*>(vreg_addr);
+ return reinterpret_cast<uint32_t*>(vreg_addr);
}
uintptr_t GetReturnPc() const;
@@ -562,7 +567,7 @@ class StackVisitor {
static void DescribeStack(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
private:
- instrumentation::InstrumentationStackFrame GetInstrumentationStackFrame(uint32_t depth) const;
+ instrumentation::InstrumentationStackFrame& GetInstrumentationStackFrame(uint32_t depth) const;
void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index c22f2cd921..84496072b1 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -19,11 +19,32 @@
#include "thread.h"
+#include <pthread.h>
+
+#include "base/casts.h"
#include "base/mutex-inl.h"
#include "cutils/atomic-inline.h"
+#include "jni_internal.h"
namespace art {
+// Quickly access the current thread from a JNIEnv.
+static inline Thread* ThreadForEnv(JNIEnv* env) {
+ JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
+ return full_env->self;
+}
+
+inline Thread* Thread::Current() {
+ // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious
+ // that we can replace this with a direct %fs access on x86.
+ if (!is_started_) {
+ return NULL;
+ } else {
+ void* thread = pthread_getspecific(Thread::pthread_key_self_);
+ return reinterpret_cast<Thread*>(thread);
+ }
+}
+
inline ThreadState Thread::SetState(ThreadState new_state) {
// Cannot use this code to change into Runnable as changing to Runnable should fail if
// old_state_and_flags.suspend_request is true.
@@ -67,17 +88,16 @@ inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
union StateAndFlags new_state_and_flags;
do {
old_state_and_flags = state_and_flags_;
+ if (UNLIKELY((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0)) {
+ RunCheckpointFunction();
+ continue;
+ }
// Copy over flags and try to clear the checkpoint bit if it is set.
new_state_and_flags.as_struct.flags = old_state_and_flags.as_struct.flags & ~kCheckpointRequest;
new_state_and_flags.as_struct.state = new_state;
// CAS the value without a memory barrier, that will occur in the unlock below.
} while (UNLIKELY(android_atomic_cas(old_state_and_flags.as_int, new_state_and_flags.as_int,
&state_and_flags_.as_int) != 0));
- // If we toggled the checkpoint flag we must have cleared it.
- uint16_t flag_change = new_state_and_flags.as_struct.flags ^ old_state_and_flags.as_struct.flags;
- if (UNLIKELY((flag_change & kCheckpointRequest) != 0)) {
- RunCheckpointFunction();
- }
// Release share on mutator_lock_.
Locks::mutator_lock_->SharedUnlock(this);
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index e8326ea982..9751076235 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -108,6 +108,12 @@ void Thread::InitTlsEntryPoints() {
&quick_entrypoints_);
}
+void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints);
+
+void Thread::ResetQuickAllocEntryPointsForThread() {
+ ResetQuickAllocEntryPoints(&quick_entrypoints_);
+}
+
void Thread::SetDeoptimizationShadowFrame(ShadowFrame* sf) {
deoptimization_shadow_frame_ = sf;
}
@@ -305,7 +311,7 @@ void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
CHECK_PTHREAD_CALL(pthread_setspecific, (Thread::pthread_key_self_, this), "attach self");
DCHECK_EQ(Thread::Current(), this);
- thin_lock_id_ = thread_list->AllocThreadId(this);
+ thin_lock_thread_id_ = thread_list->AllocThreadId(this);
InitStackHwm();
jni_env_ = new JNIEnvExt(this, java_vm);
@@ -470,9 +476,9 @@ void Thread::InitStackHwm() {
void Thread::ShortDump(std::ostream& os) const {
os << "Thread[";
- if (GetThinLockId() != 0) {
+ if (GetThreadId() != 0) {
// If we're in kStarting, we won't have a thin lock id or tid yet.
- os << GetThinLockId()
+ os << GetThreadId()
<< ",tid=" << GetTid() << ',';
}
os << GetState()
@@ -568,18 +574,32 @@ void Thread::RunCheckpointFunction() {
ATRACE_BEGIN("Checkpoint function");
checkpoint_function_->Run(this);
ATRACE_END();
+ checkpoint_function_ = NULL;
+ AtomicClearFlag(kCheckpointRequest);
}
bool Thread::RequestCheckpoint(Closure* function) {
- CHECK(!ReadFlag(kCheckpointRequest)) << "Already have a pending checkpoint request";
- checkpoint_function_ = function;
union StateAndFlags old_state_and_flags = state_and_flags_;
+ if (old_state_and_flags.as_struct.state != kRunnable) {
+ return false; // Fail, thread is suspended and so can't run a checkpoint.
+ }
+ if ((old_state_and_flags.as_struct.flags & kCheckpointRequest) != 0) {
+ return false; // Fail, already a checkpoint pending.
+ }
+ CHECK(checkpoint_function_ == NULL);
+ checkpoint_function_ = function;
+ // Checkpoint function installed now install flag bit.
// We must be runnable to request a checkpoint.
old_state_and_flags.as_struct.state = kRunnable;
union StateAndFlags new_state_and_flags = old_state_and_flags;
new_state_and_flags.as_struct.flags |= kCheckpointRequest;
int succeeded = android_atomic_cmpxchg(old_state_and_flags.as_int, new_state_and_flags.as_int,
&state_and_flags_.as_int);
+ if (UNLIKELY(succeeded != 0)) {
+ // The thread changed state before the checkpoint was installed.
+ CHECK(checkpoint_function_ == function);
+ checkpoint_function_ = NULL;
+ }
return succeeded == 0;
}
@@ -594,88 +614,6 @@ void Thread::FullSuspendCheck() {
VLOG(threads) << this << " self-reviving";
}
-Thread* Thread::SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out) {
- static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
- useconds_t total_delay_us = 0;
- useconds_t delay_us = 0;
- bool did_suspend_request = false;
- *timed_out = false;
- while (true) {
- Thread* thread;
- {
- ScopedObjectAccess soa(Thread::Current());
- Thread* self = soa.Self();
- MutexLock mu(self, *Locks::thread_list_lock_);
- thread = Thread::FromManagedThread(soa, peer);
- if (thread == NULL) {
- JNIEnv* env = self->GetJniEnv();
- ScopedLocalRef<jstring> scoped_name_string(env,
- (jstring)env->GetObjectField(peer,
- WellKnownClasses::java_lang_Thread_name));
- ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
- if (scoped_name_chars.c_str() == NULL) {
- LOG(WARNING) << "No such thread for suspend: " << peer;
- env->ExceptionClear();
- } else {
- LOG(WARNING) << "No such thread for suspend: " << peer << ":" << scoped_name_chars.c_str();
- }
-
- return NULL;
- }
- {
- MutexLock mu(soa.Self(), *Locks::thread_suspend_count_lock_);
- if (request_suspension) {
- thread->ModifySuspendCount(soa.Self(), +1, true /* for_debugger */);
- request_suspension = false;
- did_suspend_request = true;
- }
- // IsSuspended on the current thread will fail as the current thread is changed into
- // Runnable above. As the suspend count is now raised if this is the current thread
- // it will self suspend on transition to Runnable, making it hard to work with. It's simpler
- // to just explicitly handle the current thread in the callers to this code.
- CHECK_NE(thread, soa.Self()) << "Attempt to suspend the current thread for the debugger";
- // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend
- // count, or else we've waited and it has self suspended) or is the current thread, we're
- // done.
- if (thread->IsSuspended()) {
- return thread;
- }
- if (total_delay_us >= kTimeoutUs) {
- LOG(ERROR) << "Thread suspension timed out: " << peer;
- if (did_suspend_request) {
- thread->ModifySuspendCount(soa.Self(), -1, true /* for_debugger */);
- }
- *timed_out = true;
- return NULL;
- }
- }
- // Release locks and come out of runnable state.
- }
- for (int i = kLockLevelCount - 1; i >= 0; --i) {
- BaseMutex* held_mutex = Thread::Current()->GetHeldMutex(static_cast<LockLevel>(i));
- if (held_mutex != NULL) {
- LOG(FATAL) << "Holding " << held_mutex->GetName()
- << " while sleeping for thread suspension";
- }
- }
- {
- useconds_t new_delay_us = delay_us * 2;
- CHECK_GE(new_delay_us, delay_us);
- if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s.
- delay_us = new_delay_us;
- }
- }
- if (delay_us == 0) {
- sched_yield();
- // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep).
- delay_us = 500;
- } else {
- usleep(delay_us);
- total_delay_us += delay_us;
- }
- }
-}
-
void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) {
std::string group_name;
int priority;
@@ -712,7 +650,7 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) {
os << " daemon";
}
os << " prio=" << priority
- << " tid=" << thread->GetThinLockId()
+ << " tid=" << thread->GetThreadId()
<< " " << thread->GetState();
if (thread->IsStillStarting()) {
os << " (still starting up)";
@@ -962,9 +900,9 @@ Thread::Thread(bool daemon)
jpeer_(NULL),
stack_begin_(NULL),
stack_size_(0),
+ thin_lock_thread_id_(0),
stack_trace_sample_(NULL),
trace_clock_base_(0),
- thin_lock_id_(0),
tid_(0),
wait_mutex_(new Mutex("a thread wait mutex")),
wait_cond_(new ConditionVariable("a thread wait condition variable", *wait_mutex_)),
@@ -1012,9 +950,10 @@ void Thread::AssertNoPendingException() const {
}
}
-static void MonitorExitVisitor(const mirror::Object* object, void* arg) NO_THREAD_SAFETY_ANALYSIS {
+static mirror::Object* MonitorExitVisitor(mirror::Object* object, void* arg)
+ NO_THREAD_SAFETY_ANALYSIS {
Thread* self = reinterpret_cast<Thread*>(arg);
- mirror::Object* entered_monitor = const_cast<mirror::Object*>(object);
+ mirror::Object* entered_monitor = object;
if (self->HoldsLock(entered_monitor)) {
LOG(WARNING) << "Calling MonitorExit on object "
<< object << " (" << PrettyTypeOf(object) << ")"
@@ -1022,6 +961,7 @@ static void MonitorExitVisitor(const mirror::Object* object, void* arg) NO_THREA
<< *Thread::Current() << " which is detaching";
entered_monitor->MonitorExit(self);
}
+ return object;
}
void Thread::Destroy() {
@@ -1151,8 +1091,12 @@ void Thread::SirtVisitRoots(RootVisitor* visitor, void* arg) {
size_t num_refs = cur->NumberOfReferences();
for (size_t j = 0; j < num_refs; j++) {
mirror::Object* object = cur->GetReference(j);
- if (object != NULL) {
- visitor(object, arg);
+ if (object != nullptr) {
+ const mirror::Object* new_obj = visitor(object, arg);
+ DCHECK(new_obj != nullptr);
+ if (new_obj != object) {
+ cur->SetReference(j, const_cast<mirror::Object*>(new_obj));
+ }
}
}
}
@@ -1381,24 +1325,23 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job
// Transition into runnable state to work on Object*/Array*
ScopedObjectAccess soa(env);
// Decode the internal stack trace into the depth, method trace and PC trace
- mirror::ObjectArray<mirror::Object>* method_trace =
- soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal);
- int32_t depth = method_trace->GetLength() - 1;
- mirror::IntArray* pc_trace = down_cast<mirror::IntArray*>(method_trace->Get(depth));
+ int32_t depth = soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal)->GetLength() - 1;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
jobjectArray result;
- mirror::ObjectArray<mirror::StackTraceElement>* java_traces;
+
if (output_array != NULL) {
// Reuse the array we were given.
result = output_array;
- java_traces = soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(output_array);
// ...adjusting the number of frames we'll write to not exceed the array length.
- depth = std::min(depth, java_traces->GetLength());
+ const int32_t traces_length =
+ soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->GetLength();
+ depth = std::min(depth, traces_length);
} else {
// Create java_trace array and place in local reference table
- java_traces = class_linker->AllocStackTraceElementArray(soa.Self(), depth);
+ mirror::ObjectArray<mirror::StackTraceElement>* java_traces =
+ class_linker->AllocStackTraceElementArray(soa.Self(), depth);
if (java_traces == NULL) {
return NULL;
}
@@ -1411,9 +1354,12 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job
MethodHelper mh;
for (int32_t i = 0; i < depth; ++i) {
+ mirror::ObjectArray<mirror::Object>* method_trace =
+ soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal);
// Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
mirror::ArtMethod* method = down_cast<mirror::ArtMethod*>(method_trace->Get(i));
mh.ChangeMethod(method);
+ mirror::IntArray* pc_trace = down_cast<mirror::IntArray*>(method_trace->Get(depth));
uint32_t dex_pc = pc_trace->Get(i);
int32_t line_number = mh.GetLineNumFromDexPC(dex_pc);
// Allocate element, potentially triggering GC
@@ -1436,8 +1382,9 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job
return NULL;
}
const char* source_file = mh.GetDeclaringClassSourceFile();
- SirtRef<mirror::String> source_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(),
- source_file));
+ SirtRef<mirror::String> source_name_object(soa.Self(),
+ mirror::String::AllocFromModifiedUtf8(soa.Self(),
+ source_file));
mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(soa.Self(),
class_name_object.get(),
method_name_object.get(),
@@ -1446,13 +1393,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job
if (obj == NULL) {
return NULL;
}
-#ifdef MOVING_GARBAGE_COLLECTOR
- // Re-read after potential GC
- java_traces = Decode<ObjectArray<Object>*>(soa.Env(), result);
- method_trace = down_cast<ObjectArray<Object>*>(Decode<Object*>(soa.Env(), internal));
- pc_trace = down_cast<IntArray*>(method_trace->Get(depth));
-#endif
- java_traces->Set(i, obj);
+ soa.Decode<mirror::ObjectArray<mirror::StackTraceElement>*>(result)->Set(i, obj);
}
return result;
}
@@ -1614,6 +1555,7 @@ static const EntryPointInfo gThreadEntryPointInfo[] = {
INTERPRETER_ENTRY_POINT_INFO(pInterpreterToInterpreterBridge),
INTERPRETER_ENTRY_POINT_INFO(pInterpreterToCompiledCodeBridge),
JNI_ENTRY_POINT_INFO(pDlsymLookup),
+ PORTABLE_ENTRY_POINT_INFO(pPortableImtConflictTrampoline),
PORTABLE_ENTRY_POINT_INFO(pPortableResolutionTrampoline),
PORTABLE_ENTRY_POINT_INFO(pPortableToInterpreterBridge),
QUICK_ENTRY_POINT_INFO(pAllocArray),
@@ -1623,7 +1565,6 @@ static const EntryPointInfo gThreadEntryPointInfo[] = {
QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray),
QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck),
QUICK_ENTRY_POINT_INFO(pInstanceofNonTrivial),
- QUICK_ENTRY_POINT_INFO(pCanPutArrayElement),
QUICK_ENTRY_POINT_INFO(pCheckCast),
QUICK_ENTRY_POINT_INFO(pInitializeStaticStorage),
QUICK_ENTRY_POINT_INFO(pInitializeTypeAndVerifyAccess),
@@ -1641,6 +1582,9 @@ static const EntryPointInfo gThreadEntryPointInfo[] = {
QUICK_ENTRY_POINT_INFO(pGet64Static),
QUICK_ENTRY_POINT_INFO(pGetObjInstance),
QUICK_ENTRY_POINT_INFO(pGetObjStatic),
+ QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck),
+ QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck),
+ QUICK_ENTRY_POINT_INFO(pAputObject),
QUICK_ENTRY_POINT_INFO(pHandleFillArrayData),
QUICK_ENTRY_POINT_INFO(pJniMethodStart),
QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized),
@@ -1665,7 +1609,7 @@ static const EntryPointInfo gThreadEntryPointInfo[] = {
QUICK_ENTRY_POINT_INFO(pD2l),
QUICK_ENTRY_POINT_INFO(pF2l),
QUICK_ENTRY_POINT_INFO(pLdiv),
- QUICK_ENTRY_POINT_INFO(pLdivmod),
+ QUICK_ENTRY_POINT_INFO(pLmod),
QUICK_ENTRY_POINT_INFO(pLmul),
QUICK_ENTRY_POINT_INFO(pShlLong),
QUICK_ENTRY_POINT_INFO(pShrLong),
@@ -1674,10 +1618,10 @@ static const EntryPointInfo gThreadEntryPointInfo[] = {
QUICK_ENTRY_POINT_INFO(pMemcmp16),
QUICK_ENTRY_POINT_INFO(pStringCompareTo),
QUICK_ENTRY_POINT_INFO(pMemcpy),
+ QUICK_ENTRY_POINT_INFO(pQuickImtConflictTrampoline),
QUICK_ENTRY_POINT_INFO(pQuickResolutionTrampoline),
QUICK_ENTRY_POINT_INFO(pQuickToInterpreterBridge),
QUICK_ENTRY_POINT_INFO(pInvokeDirectTrampolineWithAccessCheck),
- QUICK_ENTRY_POINT_INFO(pInvokeInterfaceTrampoline),
QUICK_ENTRY_POINT_INFO(pInvokeInterfaceTrampolineWithAccessCheck),
QUICK_ENTRY_POINT_INFO(pInvokeStaticTrampolineWithAccessCheck),
QUICK_ENTRY_POINT_INFO(pInvokeSuperTrampolineWithAccessCheck),
@@ -1709,7 +1653,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset, size_t size_of_
DO_THREAD_OFFSET(self_);
DO_THREAD_OFFSET(stack_end_);
DO_THREAD_OFFSET(suspend_count_);
- DO_THREAD_OFFSET(thin_lock_id_);
+ DO_THREAD_OFFSET(thin_lock_thread_id_);
// DO_THREAD_OFFSET(top_of_managed_stack_);
// DO_THREAD_OFFSET(top_of_managed_stack_pc_);
DO_THREAD_OFFSET(top_sirt_);
@@ -1992,7 +1936,7 @@ bool Thread::HoldsLock(mirror::Object* object) {
if (object == NULL) {
return false;
}
- return object->GetThinLockId() == thin_lock_id_;
+ return object->GetLockOwnerThreadId() == thin_lock_thread_id_;
}
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor).
@@ -2016,8 +1960,11 @@ class ReferenceMapVisitor : public StackVisitor {
// SIRT for JNI or References for interpreter.
for (size_t reg = 0; reg < num_regs; ++reg) {
mirror::Object* ref = shadow_frame->GetVRegReference(reg);
- if (ref != NULL) {
- visitor_(ref, reg, this);
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (new_ref != ref) {
+ shadow_frame->SetVRegReference(reg, new_ref);
+ }
}
}
} else {
@@ -2037,8 +1984,11 @@ class ReferenceMapVisitor : public StackVisitor {
for (size_t reg = 0; reg < num_regs; ++reg) {
if (TestBitmap(reg, reg_bitmap)) {
mirror::Object* ref = shadow_frame->GetVRegReference(reg);
- if (ref != NULL) {
- visitor_(ref, reg, this);
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (new_ref != ref) {
+ shadow_frame->SetVRegReference(reg, new_ref);
+ }
}
}
}
@@ -2069,19 +2019,25 @@ class ReferenceMapVisitor : public StackVisitor {
// Does this register hold a reference?
if (TestBitmap(reg, reg_bitmap)) {
uint32_t vmap_offset;
- mirror::Object* ref;
if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) {
- uintptr_t val = GetGPR(vmap_table.ComputeRegister(core_spills, vmap_offset,
- kReferenceVReg));
- ref = reinterpret_cast<mirror::Object*>(val);
+ int vmap_reg = vmap_table.ComputeRegister(core_spills, vmap_offset, kReferenceVReg);
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(GetGPR(vmap_reg));
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (ref != new_ref) {
+ SetGPR(vmap_reg, reinterpret_cast<uintptr_t>(new_ref));
+ }
+ }
} else {
- ref = reinterpret_cast<mirror::Object*>(GetVReg(cur_quick_frame, code_item,
- core_spills, fp_spills, frame_size,
- reg));
- }
-
- if (ref != NULL) {
- visitor_(ref, reg, this);
+ uint32_t* reg_addr =
+ GetVRegAddr(cur_quick_frame, code_item, core_spills, fp_spills, frame_size, reg);
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(*reg_addr);
+ if (ref != nullptr) {
+ mirror::Object* new_ref = visitor_(ref, reg, this);
+ if (ref != new_ref) {
+ *reg_addr = reinterpret_cast<uint32_t>(new_ref);
+ }
+ }
}
}
}
@@ -2107,8 +2063,8 @@ class RootCallbackVisitor {
public:
RootCallbackVisitor(RootVisitor* visitor, void* arg) : visitor_(visitor), arg_(arg) {}
- void operator()(const mirror::Object* obj, size_t, const StackVisitor*) const {
- visitor_(obj, arg_);
+ mirror::Object* operator()(mirror::Object* obj, size_t, const StackVisitor*) const {
+ return visitor_(obj, arg_);
}
private:
@@ -2132,67 +2088,17 @@ class VerifyCallbackVisitor {
void* const arg_;
};
-struct VerifyRootWrapperArg {
- VerifyRootVisitor* visitor;
- void* arg;
-};
-
-static void VerifyRootWrapperCallback(const mirror::Object* root, void* arg) {
- VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
- wrapperArg->visitor(root, wrapperArg->arg, 0, NULL);
-}
-
-void Thread::VerifyRoots(VerifyRootVisitor* visitor, void* arg) {
- // We need to map from a RootVisitor to VerifyRootVisitor, so pass in nulls for arguments we
- // don't have.
- VerifyRootWrapperArg wrapperArg;
- wrapperArg.arg = arg;
- wrapperArg.visitor = visitor;
-
- if (opeer_ != NULL) {
- VerifyRootWrapperCallback(opeer_, &wrapperArg);
- }
- if (exception_ != NULL) {
- VerifyRootWrapperCallback(exception_, &wrapperArg);
- }
- throw_location_.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
- if (class_loader_override_ != NULL) {
- VerifyRootWrapperCallback(class_loader_override_, &wrapperArg);
- }
- jni_env_->locals.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
- jni_env_->monitors.VisitRoots(VerifyRootWrapperCallback, &wrapperArg);
-
- SirtVisitRoots(VerifyRootWrapperCallback, &wrapperArg);
-
- // Visit roots on this thread's stack
- Context* context = GetLongJumpContext();
- VerifyCallbackVisitor visitorToCallback(visitor, arg);
- ReferenceMapVisitor<VerifyCallbackVisitor> mapper(this, context, visitorToCallback);
- mapper.WalkStack();
- ReleaseLongJumpContext(context);
-
- std::deque<instrumentation::InstrumentationStackFrame>* instrumentation_stack = GetInstrumentationStack();
- typedef std::deque<instrumentation::InstrumentationStackFrame>::const_iterator It;
- for (It it = instrumentation_stack->begin(), end = instrumentation_stack->end(); it != end; ++it) {
- mirror::Object* this_object = (*it).this_object_;
- if (this_object != NULL) {
- VerifyRootWrapperCallback(this_object, &wrapperArg);
- }
- mirror::ArtMethod* method = (*it).method_;
- VerifyRootWrapperCallback(method, &wrapperArg);
- }
-}
-
void Thread::VisitRoots(RootVisitor* visitor, void* arg) {
- if (opeer_ != NULL) {
- visitor(opeer_, arg);
+ if (opeer_ != nullptr) {
+ opeer_ = visitor(opeer_, arg);
}
- if (exception_ != NULL) {
- visitor(exception_, arg);
+ if (exception_ != nullptr) {
+ exception_ = reinterpret_cast<mirror::Throwable*>(visitor(exception_, arg));
}
throw_location_.VisitRoots(visitor, arg);
- if (class_loader_override_ != NULL) {
- visitor(class_loader_override_, arg);
+ if (class_loader_override_ != nullptr) {
+ class_loader_override_ = reinterpret_cast<mirror::ClassLoader*>(
+ visitor(class_loader_override_, arg));
}
jni_env_->locals.VisitRoots(visitor, arg);
jni_env_->monitors.VisitRoots(visitor, arg);
@@ -2206,24 +2112,26 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) {
mapper.WalkStack();
ReleaseLongJumpContext(context);
- for (const instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
- mirror::Object* this_object = frame.this_object_;
- if (this_object != NULL) {
- visitor(this_object, arg);
+ for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
+ if (frame.this_object_ != nullptr) {
+ frame.this_object_ = visitor(frame.this_object_, arg);
+ DCHECK(frame.this_object_ != nullptr);
}
- mirror::ArtMethod* method = frame.method_;
- visitor(method, arg);
+ frame.method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(frame.method_, arg));
+ DCHECK(frame.method_ != nullptr);
}
}
-static void VerifyObject(const mirror::Object* root, void* arg) {
- gc::Heap* heap = reinterpret_cast<gc::Heap*>(arg);
- heap->VerifyObject(root);
+static mirror::Object* VerifyRoot(mirror::Object* root, void* arg) {
+ DCHECK(root != nullptr);
+ DCHECK(arg != nullptr);
+ reinterpret_cast<gc::Heap*>(arg)->VerifyObject(root);
+ return root;
}
void Thread::VerifyStackImpl() {
UniquePtr<Context> context(Context::Create());
- RootCallbackVisitor visitorToCallback(VerifyObject, Runtime::Current()->GetHeap());
+ RootCallbackVisitor visitorToCallback(VerifyRoot, Runtime::Current()->GetHeap());
ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context.get(), visitorToCallback);
mapper.WalkStack();
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 40e3f5fbb2..3aa137375e 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -17,8 +17,6 @@
#ifndef ART_RUNTIME_THREAD_H_
#define ART_RUNTIME_THREAD_H_
-#include <pthread.h>
-
#include <bitset>
#include <deque>
#include <iosfwd>
@@ -104,16 +102,7 @@ class PACKED(4) Thread {
// Reset internal state of child thread after fork.
void InitAfterFork();
- static Thread* Current() {
- // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious
- // that we can replace this with a direct %fs access on x86.
- if (!is_started_) {
- return NULL;
- } else {
- void* thread = pthread_getspecific(Thread::pthread_key_self_);
- return reinterpret_cast<Thread*>(thread);
- }
- }
+ static Thread* Current();
static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts,
mirror::Object* thread_peer)
@@ -165,7 +154,8 @@ class PACKED(4) Thread {
void ModifySuspendCount(Thread* self, int delta, bool for_debugger)
EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_);
- bool RequestCheckpoint(Closure* function);
+ bool RequestCheckpoint(Closure* function)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_suspend_count_lock_);
// Called when thread detected that the thread_suspend_count_ was non-zero. Gives up share of
// mutator_lock_ and waits until it is resumed and thread_suspend_count_ is zero.
@@ -186,14 +176,6 @@ class PACKED(4) Thread {
UNLOCK_FUNCTION(Locks::mutator_lock_)
ALWAYS_INLINE;
- // Wait for a debugger suspension on the thread associated with the given peer. Returns the
- // thread on success, else NULL. If the thread should be suspended then request_suspension should
- // be true on entry. If the suspension times out then *timeout is set to true.
- static Thread* SuspendForDebugger(jobject peer, bool request_suspension, bool* timed_out)
- LOCKS_EXCLUDED(Locks::mutator_lock_,
- Locks::thread_list_lock_,
- Locks::thread_suspend_count_lock_);
-
// Once called thread suspension will cause an assertion failure.
#ifndef NDEBUG
const char* StartAssertNoThreadSuspension(const char* cause) {
@@ -230,7 +212,7 @@ class PACKED(4) Thread {
return daemon_;
}
- bool HoldsLock(mirror::Object*);
+ bool HoldsLock(mirror::Object*) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
/*
* Changes the priority of this thread to match that of the java.lang.Thread object.
@@ -248,8 +230,8 @@ class PACKED(4) Thread {
*/
static int GetNativePriority();
- uint32_t GetThinLockId() const {
- return thin_lock_id_;
+ uint32_t GetThreadId() const {
+ return thin_lock_thread_id_;
}
pid_t GetTid() const {
@@ -406,9 +388,6 @@ class PACKED(4) Thread {
void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void VerifyRoots(VerifyRootVisitor* visitor, void* arg)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
void VerifyStack() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
//
@@ -428,7 +407,7 @@ class PACKED(4) Thread {
}
static ThreadOffset ThinLockIdOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_id_));
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, thin_lock_thread_id_));
}
static ThreadOffset CardTableOffset() {
@@ -590,6 +569,8 @@ class PACKED(4) Thread {
void AtomicClearFlag(ThreadFlag flag);
+ void ResetQuickAllocEntryPointsForThread();
+
private:
// We have no control over the size of 'bool', but want our boolean fields
// to be 4-byte quantities.
@@ -714,18 +695,18 @@ class PACKED(4) Thread {
// Size of the stack
size_t stack_size_;
- // Pointer to previous stack trace captured by sampling profiler.
- std::vector<mirror::ArtMethod*>* stack_trace_sample_;
-
- // The clock base used for tracing.
- uint64_t trace_clock_base_;
-
// Thin lock thread id. This is a small integer used by the thin lock implementation.
// This is not to be confused with the native thread's tid, nor is it the value returned
// by java.lang.Thread.getId --- this is a distinct value, used only for locking. One
// important difference between this id and the ids visible to managed code is that these
// ones get reused (to ensure that they fit in the number of bits available).
- uint32_t thin_lock_id_;
+ uint32_t thin_lock_thread_id_;
+
+ // Pointer to previous stack trace captured by sampling profiler.
+ std::vector<mirror::ArtMethod*>* stack_trace_sample_;
+
+ // The clock base used for tracing.
+ uint64_t trace_clock_base_;
// System thread id.
pid_t tid_;
@@ -734,13 +715,16 @@ class PACKED(4) Thread {
// Guards the 'interrupted_' and 'wait_monitor_' members.
mutable Mutex* wait_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ // Condition variable waited upon during a wait.
ConditionVariable* wait_cond_ GUARDED_BY(wait_mutex_);
- // Pointer to the monitor lock we're currently waiting on (or NULL).
+ // Pointer to the monitor lock we're currently waiting on or NULL if not waiting.
Monitor* wait_monitor_ GUARDED_BY(wait_mutex_);
// Thread "interrupted" status; stays raised until queried or thrown.
bool32_t interrupted_ GUARDED_BY(wait_mutex_);
- // The next thread in the wait set this thread is part of.
+ // The next thread in the wait set this thread is part of or NULL if not waiting.
Thread* wait_next_;
+
+
// If we're blocked in MonitorEnter, this is the object we're trying to lock.
mirror::Object* monitor_enter_object_;
@@ -797,7 +781,8 @@ class PACKED(4) Thread {
// Cause for last suspension.
const char* last_no_thread_suspension_cause_;
- // Pending checkpoint functions.
+ // Pending checkpoint function or NULL if non-pending. Installation guarding by
+ // Locks::thread_suspend_count_lock_.
Closure* checkpoint_function_;
public:
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 671924a620..ff1ed2a4d2 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -17,14 +17,22 @@
#include "thread_list.h"
#include <dirent.h>
+#include <ScopedLocalRef.h>
+#include <ScopedUtfChars.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/mutex.h"
+#include "base/mutex-inl.h"
#include "base/timing_logger.h"
#include "debugger.h"
+#include "jni_internal.h"
+#include "lock_word.h"
+#include "monitor.h"
+#include "scoped_thread_state_change.h"
#include "thread.h"
#include "utils.h"
+#include "well_known_classes.h"
namespace art {
@@ -32,6 +40,7 @@ ThreadList::ThreadList()
: allocated_ids_lock_("allocated thread ids lock"),
suspend_all_count_(0), debug_suspend_all_count_(0),
thread_exit_cond_("thread exit condition variable", *Locks::thread_list_lock_) {
+ CHECK(Monitor::IsValidLockWord(LockWord::FromThinLockId(kMaxThreadId, 1)));
}
ThreadList::~ThreadList() {
@@ -159,18 +168,19 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
// Call a checkpoint function for each thread, threads which are suspend get their checkpoint
// manually called.
MutexLock mu(self, *Locks::thread_list_lock_);
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
for (const auto& thread : list_) {
if (thread != self) {
- for (;;) {
+ while (true) {
if (thread->RequestCheckpoint(checkpoint_function)) {
// This thread will run it's checkpoint some time in the near future.
count++;
break;
} else {
// We are probably suspended, try to make sure that we stay suspended.
- MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
// The thread switched back to runnable.
if (thread->GetState() == kRunnable) {
+ // Spurious fail, try again.
continue;
}
thread->ModifySuspendCount(self, +1, false);
@@ -203,7 +213,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
}
}
// We know for sure that the thread is suspended at this point.
- thread->RunCheckpointFunction();
+ checkpoint_function->Run(thread);
{
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
thread->ModifySuspendCount(self, -1, false);
@@ -321,6 +331,178 @@ void ThreadList::Resume(Thread* thread, bool for_debugger) {
VLOG(threads) << "Resume(" << *thread << ") complete";
}
+static void ThreadSuspendByPeerWarning(Thread* self, int level, const char* message, jobject peer) {
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedLocalRef<jstring>
+ scoped_name_string(env, (jstring)env->GetObjectField(peer,
+ WellKnownClasses::java_lang_Thread_name));
+ ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
+ if (scoped_name_chars.c_str() == NULL) {
+ LOG(level) << message << ": " << peer;
+ env->ExceptionClear();
+ } else {
+ LOG(level) << message << ": " << peer << ":" << scoped_name_chars.c_str();
+ }
+}
+
+// Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an
+// individual thread requires polling. delay_us is the requested sleep and total_delay_us
+// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield,
+// subsequently sleeps increase delay_us from 1ms to 500ms by doubling.
+static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) {
+ for (int i = kLockLevelCount - 1; i >= 0; --i) {
+ BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
+ if (held_mutex != NULL) {
+ LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension";
+ }
+ }
+ {
+ useconds_t new_delay_us = (*delay_us) * 2;
+ CHECK_GE(new_delay_us, *delay_us);
+ if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s.
+ *delay_us = new_delay_us;
+ }
+ }
+ if ((*delay_us) == 0) {
+ sched_yield();
+ // Default to 1 milliseconds (note that this gets multiplied by 2 before the first sleep).
+ (*delay_us) = 500;
+ } else {
+ usleep(*delay_us);
+ (*total_delay_us) += (*delay_us);
+ }
+}
+
+Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
+ bool debug_suspension, bool* timed_out) {
+ static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
+ useconds_t total_delay_us = 0;
+ useconds_t delay_us = 0;
+ bool did_suspend_request = false;
+ *timed_out = false;
+ Thread* self = Thread::Current();
+ while (true) {
+ Thread* thread;
+ {
+ ScopedObjectAccess soa(self);
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ thread = Thread::FromManagedThread(soa, peer);
+ if (thread == NULL) {
+ ThreadSuspendByPeerWarning(self, WARNING, "No such thread for suspend", peer);
+ return NULL;
+ }
+ {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ if (request_suspension) {
+ thread->ModifySuspendCount(self, +1, debug_suspension);
+ request_suspension = false;
+ did_suspend_request = true;
+ } else {
+ // If the caller isn't requesting suspension, a suspension should have already occurred.
+ CHECK_GT(thread->GetSuspendCount(), 0);
+ }
+ // IsSuspended on the current thread will fail as the current thread is changed into
+ // Runnable above. As the suspend count is now raised if this is the current thread
+ // it will self suspend on transition to Runnable, making it hard to work with. It's simpler
+ // to just explicitly handle the current thread in the callers to this code.
+ CHECK_NE(thread, self) << "Attempt to suspend the current thread for the debugger";
+ // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend
+ // count, or else we've waited and it has self suspended) or is the current thread, we're
+ // done.
+ if (thread->IsSuspended()) {
+ return thread;
+ }
+ if (total_delay_us >= kTimeoutUs) {
+ ThreadSuspendByPeerWarning(self, ERROR, "Thread suspension timed out", peer);
+ if (did_suspend_request) {
+ thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+ }
+ *timed_out = true;
+ return NULL;
+ }
+ }
+ // Release locks and come out of runnable state.
+ }
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+ }
+}
+
+static void ThreadSuspendByThreadIdWarning(int level, const char* message, uint32_t thread_id) {
+ LOG(level) << StringPrintf("%s: %d", message, thread_id);
+}
+
+Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension,
+ bool* timed_out) {
+ static const useconds_t kTimeoutUs = 30 * 1000000; // 30s.
+ useconds_t total_delay_us = 0;
+ useconds_t delay_us = 0;
+ bool did_suspend_request = false;
+ *timed_out = false;
+ Thread* self = Thread::Current();
+ CHECK_NE(thread_id, kInvalidThreadId);
+ while (true) {
+ Thread* thread = NULL;
+ {
+ ScopedObjectAccess soa(self);
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ for (const auto& it : list_) {
+ if (it->GetThreadId() == thread_id) {
+ thread = it;
+ break;
+ }
+ }
+ if (thread == NULL) {
+ // There's a race in inflating a lock and the owner giving up ownership and then dying.
+ ThreadSuspendByThreadIdWarning(WARNING, "No such thread id for suspend", thread_id);
+ return NULL;
+ }
+ {
+ MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+ if (!did_suspend_request) {
+ thread->ModifySuspendCount(self, +1, debug_suspension);
+ did_suspend_request = true;
+ } else {
+ // If the caller isn't requesting suspension, a suspension should have already occurred.
+ CHECK_GT(thread->GetSuspendCount(), 0);
+ }
+ // IsSuspended on the current thread will fail as the current thread is changed into
+ // Runnable above. As the suspend count is now raised if this is the current thread
+ // it will self suspend on transition to Runnable, making it hard to work with. It's simpler
+ // to just explicitly handle the current thread in the callers to this code.
+ CHECK_NE(thread, self) << "Attempt to suspend the current thread for the debugger";
+ // If thread is suspended (perhaps it was already not Runnable but didn't have a suspend
+ // count, or else we've waited and it has self suspended) or is the current thread, we're
+ // done.
+ if (thread->IsSuspended()) {
+ return thread;
+ }
+ if (total_delay_us >= kTimeoutUs) {
+ ThreadSuspendByThreadIdWarning(ERROR, "Thread suspension timed out", thread_id);
+ if (did_suspend_request) {
+ thread->ModifySuspendCount(soa.Self(), -1, debug_suspension);
+ }
+ *timed_out = true;
+ return NULL;
+ }
+ }
+ // Release locks and come out of runnable state.
+ }
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
+ }
+}
+
+Thread* ThreadList::FindThreadByThreadId(uint32_t thin_lock_id) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::thread_list_lock_);
+ for (const auto& thread : list_) {
+ if (thread->GetThreadId() == thin_lock_id) {
+ CHECK(thread == self || thread->IsSuspended());
+ return thread;
+ }
+ }
+ return NULL;
+}
+
void ThreadList::SuspendAllForDebugger() {
Thread* self = Thread::Current();
Thread* debug_thread = Dbg::GetDebugThread();
@@ -527,8 +709,8 @@ void ThreadList::Unregister(Thread* self) {
// suspend and so on, must happen at this point, and not in ~Thread.
self->Destroy();
- uint32_t thin_lock_id = self->thin_lock_id_;
- self->thin_lock_id_ = 0;
+ uint32_t thin_lock_id = self->thin_lock_thread_id_;
+ self->thin_lock_thread_id_ = 0;
ReleaseThreadId(self, thin_lock_id);
while (self != NULL) {
// Remove and delete the Thread* while holding the thread_list_lock_ and
@@ -568,10 +750,24 @@ void ThreadList::VisitRoots(RootVisitor* visitor, void* arg) const {
}
}
+struct VerifyRootWrapperArg {
+ VerifyRootVisitor* visitor;
+ void* arg;
+};
+
+static mirror::Object* VerifyRootWrapperCallback(mirror::Object* root, void* arg) {
+ VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
+ wrapperArg->visitor(root, wrapperArg->arg, 0, NULL);
+ return root;
+}
+
void ThreadList::VerifyRoots(VerifyRootVisitor* visitor, void* arg) const {
+ VerifyRootWrapperArg wrapper;
+ wrapper.visitor = visitor;
+ wrapper.arg = arg;
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
for (const auto& thread : list_) {
- thread->VerifyRoots(visitor, arg);
+ thread->VisitRoots(VerifyRootWrapperCallback, &wrapper);
}
}
@@ -594,14 +790,4 @@ void ThreadList::ReleaseThreadId(Thread* self, uint32_t id) {
allocated_ids_.reset(id);
}
-Thread* ThreadList::FindThreadByThinLockId(uint32_t thin_lock_id) {
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- for (const auto& thread : list_) {
- if (thread->GetThinLockId() == thin_lock_id) {
- return thread;
- }
- }
- return NULL;
-}
-
} // namespace art
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 3df3e2c841..b1b3e88860 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_THREAD_LIST_H_
#include "base/mutex.h"
+#include "jni.h"
#include "root_visitor.h"
#include <bitset>
@@ -31,8 +32,8 @@ class TimingLogger;
class ThreadList {
public:
static const uint32_t kMaxThreadId = 0xFFFF;
- static const uint32_t kInvalidId = 0;
- static const uint32_t kMainId = 1;
+ static const uint32_t kInvalidThreadId = 0;
+ static const uint32_t kMainThreadId = 1;
explicit ThreadList();
~ThreadList();
@@ -59,6 +60,30 @@ class ThreadList {
LOCKS_EXCLUDED(Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
+
+ // Suspend a thread using a peer, typically used by the debugger. Returns the thread on success,
+ // else NULL. The peer is used to identify the thread to avoid races with the thread terminating.
+ // If the thread should be suspended then value of request_suspension should be true otherwise
+ // the routine will wait for a previous suspend request. If the suspension times out then *timeout
+ // is set to true.
+ static Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension,
+ bool* timed_out)
+ LOCKS_EXCLUDED(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
+ // thread on success else NULL. The thread id is used to identify the thread to avoid races with
+ // 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)
+ LOCKS_EXCLUDED(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.
size_t RunCheckpoint(Closure* checkpoint_function);
@@ -99,8 +124,6 @@ class ThreadList {
return list_;
}
- Thread* FindThreadByThinLockId(uint32_t thin_lock_id);
-
private:
uint32_t AllocThreadId(Thread* self);
void ReleaseThreadId(Thread* self, uint32_t id) LOCKS_EXCLUDED(allocated_ids_lock_);
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
index e428511d48..01497ef0e5 100644
--- a/runtime/throw_location.cc
+++ b/runtime/throw_location.cc
@@ -34,11 +34,14 @@ std::string ThrowLocation::Dump() const {
}
void ThrowLocation::VisitRoots(RootVisitor* visitor, void* arg) {
- if (this_object_ != NULL) {
- visitor(this_object_, arg);
+ if (this_object_ != nullptr) {
+ this_object_ = const_cast<mirror::Object*>(visitor(this_object_, arg));
+ DCHECK(this_object_ != nullptr);
}
- if (method_ != NULL) {
- visitor(method_, arg);
+ if (method_ != nullptr) {
+ method_ = const_cast<mirror::ArtMethod*>(
+ reinterpret_cast<const mirror::ArtMethod*>(visitor(method_, arg)));
+ DCHECK(method_ != nullptr);
}
}
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 7b25306177..ec95a87146 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -667,7 +667,7 @@ void Trace::DumpMethodList(std::ostream& os, const std::set<mirror::ArtMethod*>&
mh.ChangeMethod(method);
os << StringPrintf("%p\t%s\t%s\t%s\t%s\n", method,
PrettyDescriptor(mh.GetDeclaringClassDescriptor()).c_str(), mh.GetName(),
- mh.GetSignature().c_str(), mh.GetDeclaringClassSourceFile());
+ mh.GetSignature().ToString().c_str(), mh.GetDeclaringClassSourceFile());
}
}
diff --git a/runtime/utf-inl.h b/runtime/utf-inl.h
new file mode 100644
index 0000000000..d8c258b5d9
--- /dev/null
+++ b/runtime/utf-inl.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_UTF_INL_H_
+#define ART_RUNTIME_UTF_INL_H_
+
+#include "utf.h"
+
+namespace art {
+
+inline uint16_t GetUtf16FromUtf8(const char** utf8_data_in) {
+ uint8_t one = *(*utf8_data_in)++;
+ if ((one & 0x80) == 0) {
+ // one-byte encoding
+ return one;
+ }
+ // two- or three-byte encoding
+ uint8_t two = *(*utf8_data_in)++;
+ if ((one & 0x20) == 0) {
+ // two-byte encoding
+ return ((one & 0x1f) << 6) | (two & 0x3f);
+ }
+ // three-byte encoding
+ uint8_t three = *(*utf8_data_in)++;
+ return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+}
+
+inline int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1,
+ const char* utf8_2) {
+ for (;;) {
+ if (*utf8_1 == '\0') {
+ return (*utf8_2 == '\0') ? 0 : -1;
+ } else if (*utf8_2 == '\0') {
+ return 1;
+ }
+
+ int c1 = GetUtf16FromUtf8(&utf8_1);
+ int c2 = GetUtf16FromUtf8(&utf8_2);
+
+ if (c1 != c2) {
+ return c1 > c2 ? 1 : -1;
+ }
+ }
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_UTF_INL_H_
diff --git a/runtime/utf.cc b/runtime/utf.cc
index 1add7d9a68..5ec2ea1c36 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -19,6 +19,7 @@
#include "base/logging.h"
#include "mirror/array.h"
#include "mirror/object-inl.h"
+#include "utf-inl.h"
namespace art {
@@ -84,41 +85,6 @@ int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count) {
return hash;
}
-
-uint16_t GetUtf16FromUtf8(const char** utf8_data_in) {
- uint8_t one = *(*utf8_data_in)++;
- if ((one & 0x80) == 0) {
- // one-byte encoding
- return one;
- }
- // two- or three-byte encoding
- uint8_t two = *(*utf8_data_in)++;
- if ((one & 0x20) == 0) {
- // two-byte encoding
- return ((one & 0x1f) << 6) | (two & 0x3f);
- }
- // three-byte encoding
- uint8_t three = *(*utf8_data_in)++;
- return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
-}
-
-int CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(const char* utf8_1, const char* utf8_2) {
- for (;;) {
- if (*utf8_1 == '\0') {
- return (*utf8_2 == '\0') ? 0 : -1;
- } else if (*utf8_2 == '\0') {
- return 1;
- }
-
- int c1 = GetUtf16FromUtf8(&utf8_1);
- int c2 = GetUtf16FromUtf8(&utf8_2);
-
- if (c1 != c2) {
- return c1 > c2 ? 1 : -1;
- }
- }
-}
-
int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) {
for (;;) {
if (*utf8_1 == '\0') {
diff --git a/runtime/utf.h b/runtime/utf.h
index 4c9a1d959e..cc5e6d48c2 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -29,9 +29,10 @@
* See http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8 for the details.
*/
namespace art {
+
namespace mirror {
-template<class T> class PrimitiveArray;
-typedef PrimitiveArray<uint16_t> CharArray;
+ template<class T> class PrimitiveArray;
+ typedef PrimitiveArray<uint16_t> CharArray;
} // namespace mirror
/*
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 7fa06a394f..e039581b05 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -34,7 +34,7 @@
#include "mirror/string.h"
#include "object_utils.h"
#include "os.h"
-#include "utf.h"
+#include "utf-inl.h"
#if !defined(HAVE_POSIX_CLOCKS)
#include <sys/time.h>
@@ -366,11 +366,13 @@ std::string PrettyMethod(const mirror::ArtMethod* m, bool with_signature) {
result += '.';
result += mh.GetName();
if (with_signature) {
- std::string signature(mh.GetSignature());
- if (signature == "<no signature>") {
- return result + signature;
+ const Signature signature = mh.GetSignature();
+ std::string sig_as_string(signature.ToString());
+ if (signature == Signature::NoSignature()) {
+ return result + sig_as_string;
}
- result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str());
+ result = PrettyReturnType(sig_as_string.c_str()) + " " + result +
+ PrettyArguments(sig_as_string.c_str());
}
return result;
}
@@ -384,11 +386,13 @@ std::string PrettyMethod(uint32_t method_idx, const DexFile& dex_file, bool with
result += '.';
result += dex_file.GetMethodName(method_id);
if (with_signature) {
- std::string signature(dex_file.GetMethodSignature(method_id));
- if (signature == "<no signature>") {
- return result + signature;
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ std::string sig_as_string(signature.ToString());
+ if (signature == Signature::NoSignature()) {
+ return result + sig_as_string;
}
- result = PrettyReturnType(signature.c_str()) + " " + result + PrettyArguments(signature.c_str());
+ result = PrettyReturnType(sig_as_string.c_str()) + " " + result +
+ PrettyArguments(sig_as_string.c_str());
}
return result;
}
@@ -640,7 +644,7 @@ std::string JniLongName(const mirror::ArtMethod* m) {
long_name += JniShortName(m);
long_name += "__";
- std::string signature(MethodHelper(m).GetSignature());
+ std::string signature(MethodHelper(m).GetSignature().ToString());
signature.erase(0, 1);
signature.erase(signature.begin() + signature.find(')'), signature.end());
@@ -713,9 +717,9 @@ bool IsValidPartOfMemberNameUtf8Slow(const char** pUtf8Ptr) {
* this function returns false, then the given pointer may only have
* been partially advanced.
*/
-bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
+static bool IsValidPartOfMemberNameUtf8(const char** pUtf8Ptr) {
uint8_t c = (uint8_t) **pUtf8Ptr;
- if (c <= 0x7f) {
+ if (LIKELY(c <= 0x7f)) {
// It's low-ascii, so check the table.
uint32_t wordIdx = c >> 5;
uint32_t bitIdx = c & 0x1f;
@@ -756,7 +760,7 @@ bool IsValidMemberName(const char* s) {
}
enum ClassNameType { kName, kDescriptor };
-bool IsValidClassName(const char* s, ClassNameType type, char separator) {
+static bool IsValidClassName(const char* s, ClassNameType type, char separator) {
int arrayCount = 0;
while (*s == '[') {
arrayCount++;
@@ -885,6 +889,35 @@ void Split(const std::string& s, char separator, std::vector<std::string>& resul
}
}
+std::string Trim(std::string s) {
+ std::string result;
+ unsigned int start_index = 0;
+ unsigned int end_index = s.size() - 1;
+
+ // Skip initial whitespace.
+ while (start_index < s.size()) {
+ if (!isspace(s[start_index])) {
+ break;
+ }
+ start_index++;
+ }
+
+ // Skip terminating whitespace.
+ while (end_index >= start_index) {
+ if (!isspace(s[end_index])) {
+ break;
+ }
+ end_index--;
+ }
+
+ // All spaces, no beef.
+ if (end_index < start_index) {
+ return "";
+ }
+ // Start_index is the first non-space, end_index is the last one.
+ return s.substr(start_index, end_index - start_index + 1);
+}
+
template <typename StringT>
std::string Join(std::vector<StringT>& strings, char separator) {
if (strings.empty()) {
@@ -1135,12 +1168,12 @@ std::string GetDalvikCacheOrDie(const char* android_data) {
return dalvik_cache;
}
-std::string GetDalvikCacheFilenameOrDie(const std::string& location) {
+std::string GetDalvikCacheFilenameOrDie(const char* location) {
std::string dalvik_cache(GetDalvikCacheOrDie(GetAndroidData()));
if (location[0] != '/') {
LOG(FATAL) << "Expected path in location to be absolute: "<< location;
}
- std::string cache_file(location, 1); // skip leading slash
+ std::string cache_file(&location[1]); // skip leading slash
if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) {
cache_file += "/";
cache_file += DexFile::kClassesDex;
diff --git a/runtime/utils.h b/runtime/utils.h
index 975f08b838..6850e8b025 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -119,6 +119,7 @@ struct TypeStaticIf<false, A, B> {
typedef B value;
};
+// For rounding integers.
template<typename T>
static inline T RoundDown(T x, int n) {
CHECK(IsPowerOfTwo(n));
@@ -130,6 +131,18 @@ static inline T RoundUp(T x, int n) {
return RoundDown(x + n - 1, n);
}
+// For aligning pointers.
+template<typename T>
+static inline T* AlignDown(T* x, int n) {
+ CHECK(IsPowerOfTwo(n));
+ return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(x) & -static_cast<uintptr_t>(n));
+}
+
+template<typename T>
+static inline T* AlignUp(T* x, int n) {
+ return AlignDown(reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(x) + static_cast<uintptr_t>(n - 1)), n);
+}
+
// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
// figure 3-3, page 48, where the function is called clp2.
static inline uint32_t RoundUpToPowerOfTwo(uint32_t x) {
@@ -239,7 +252,7 @@ std::string FormatDuration(uint64_t nano_duration, TimeUnit time_unit);
// Get the appropriate unit for a nanosecond duration.
TimeUnit GetAppropriateTimeUnit(uint64_t nano_duration);
-// Get the divisor to convert from a nanoseconds to a time unit
+// Get the divisor to convert from a nanoseconds to a time unit.
uint64_t GetNsToTimeUnitDivisor(TimeUnit time_unit);
// Performs JNI name mangling as described in section 11.3 "Linking Native Methods"
@@ -313,6 +326,9 @@ void InitTimeSpec(bool absolute, int clock, int64_t ms, int32_t ns, timespec* ts
// strings. Empty strings will be omitted.
void Split(const std::string& s, char separator, std::vector<std::string>& result);
+// Trims whitespace off both ends of the given string.
+std::string Trim(std::string s);
+
// Joins a vector of strings into a single string, using the given separator.
template <typename StringT> std::string Join(std::vector<StringT>& strings, char separator);
@@ -341,17 +357,17 @@ void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix = "", bool
// Dumps the kernel stack for thread 'tid' to 'os'. Note that this is only available on linux-x86.
void DumpKernelStack(std::ostream& os, pid_t tid, const char* prefix = "", bool include_count = true);
-// Find $ANDROID_ROOT, /system, or abort
+// Find $ANDROID_ROOT, /system, or abort.
const char* GetAndroidRoot();
-// Find $ANDROID_DATA, /data, or abort
+// Find $ANDROID_DATA, /data, or abort.
const char* GetAndroidData();
// Returns the dalvik-cache location, or dies trying.
std::string GetDalvikCacheOrDie(const char* android_data);
// Returns the dalvik-cache location for a DexFile or OatFile, or dies trying.
-std::string GetDalvikCacheFilenameOrDie(const std::string& location);
+std::string GetDalvikCacheFilenameOrDie(const char* location);
// Check whether the given magic matches a known file type.
bool IsZipMagic(uint32_t magic);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 09fa82988e..9f980610a4 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -51,7 +51,8 @@ void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* fl
uint32_t insns_size, uint16_t registers_size,
MethodVerifier* verifier) {
DCHECK_GT(insns_size, 0U);
-
+ register_lines_.reset(new RegisterLine*[insns_size]());
+ size_ = insns_size;
for (uint32_t i = 0; i < insns_size; i++) {
bool interesting = false;
switch (mode) {
@@ -68,7 +69,16 @@ void PcToRegisterLineTable::Init(RegisterTrackingMode mode, InstructionFlags* fl
break;
}
if (interesting) {
- pc_to_register_line_.Put(i, new RegisterLine(registers_size, verifier));
+ register_lines_[i] = RegisterLine::Create(registers_size, verifier);
+ }
+ }
+}
+
+PcToRegisterLineTable::~PcToRegisterLineTable() {
+ for (size_t i = 0; i < size_; i++) {
+ delete register_lines_[i];
+ if (kIsDebugBuild) {
+ register_lines_[i] = nullptr;
}
}
}
@@ -80,7 +90,7 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla
return kNoFailure;
}
mirror::Class* super = klass->GetSuperClass();
- if (super == NULL && StringPiece(ClassHelper(klass).GetDescriptor()) != "Ljava/lang/Object;") {
+ if (super == NULL && strcmp("Ljava/lang/Object;", ClassHelper(klass).GetDescriptor()) != 0) {
*error = "Verifier rejected class ";
*error += PrettyDescriptor(klass);
*error += " that has no super class";
@@ -293,6 +303,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca
dex_method_idx_(dex_method_idx),
mirror_method_(method),
method_access_flags_(method_access_flags),
+ return_type_(nullptr),
dex_file_(dex_file),
dex_cache_(dex_cache),
class_loader_(class_loader),
@@ -300,7 +311,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca
code_item_(code_item),
declaring_class_(NULL),
interesting_dex_pc_(-1),
- monitor_enter_dex_pcs_(NULL),
+ monitor_enter_dex_pcs_(nullptr),
have_pending_hard_failure_(false),
have_pending_runtime_throw_failure_(false),
new_instance_count_(0),
@@ -309,7 +320,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca
allow_soft_failures_(allow_soft_failures),
has_check_casts_(false),
has_virtual_or_interface_invokes_(false) {
- DCHECK(class_def != NULL);
+ DCHECK(class_def != nullptr);
}
void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc,
@@ -495,17 +506,25 @@ bool MethodVerifier::ComputeWidthsAndCountOps() {
while (dex_pc < insns_size) {
Instruction::Code opcode = inst->Opcode();
- if (opcode == Instruction::NEW_INSTANCE) {
- new_instance_count++;
- } else if (opcode == Instruction::MONITOR_ENTER) {
- monitor_enter_count++;
- } else if (opcode == Instruction::CHECK_CAST) {
- has_check_casts_ = true;
- } else if ((inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
- (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
- (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE)) {
- has_virtual_or_interface_invokes_ = true;
+ switch (opcode) {
+ case Instruction::APUT_OBJECT:
+ case Instruction::CHECK_CAST:
+ has_check_casts_ = true;
+ break;
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ has_virtual_or_interface_invokes_ = true;
+ break;
+ case Instruction::MONITOR_ENTER:
+ monitor_enter_count++;
+ break;
+ case Instruction::NEW_INSTANCE:
+ new_instance_count++;
+ break;
+ default:
+ break;
}
size_t inst_size = inst->SizeInCodeUnits();
insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size);
@@ -1034,8 +1053,8 @@ bool MethodVerifier::VerifyCodeFlow() {
this);
- work_line_.reset(new RegisterLine(registers_size, this));
- saved_line_.reset(new RegisterLine(registers_size, this));
+ work_line_.reset(RegisterLine::Create(registers_size, this));
+ saved_line_.reset(RegisterLine::Create(registers_size, this));
/* Initialize register types of method arguments. */
if (!SetTypesFromSignature()) {
@@ -1935,7 +1954,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
if (!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
!cast_type.GetClass()->IsInterface() && !cast_type.IsAssignableFrom(orig_type)) {
- RegisterLine* update_line = new RegisterLine(code_item_->registers_size_, this);
+ RegisterLine* update_line = RegisterLine::Create(code_item_->registers_size_, this);
if (inst->Opcode() == Instruction::IF_EQZ) {
fallthrough_line.reset(update_line);
} else {
@@ -2131,20 +2150,30 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
inst->Opcode() == Instruction::INVOKE_SUPER_RANGE);
mirror::ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_VIRTUAL,
is_range, is_super);
- const char* descriptor;
- if (called_method == NULL) {
+ const RegType* return_type = nullptr;
+ if (called_method != nullptr) {
+ MethodHelper mh(called_method);
+ mirror::Class* return_type_class = mh.GetReturnType();
+ if (return_type_class != nullptr) {
+ return_type = &reg_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class,
+ return_type_class->CannotBeAssignedFromOtherTypes());
+ } else {
+ Thread* self = Thread::Current();
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ }
+ if (return_type == nullptr) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
- descriptor = dex_file_->StringByTypeIdx(return_type_idx);
- } else {
- descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
+ const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx);
+ return_type = &reg_types_.FromDescriptor(class_loader_, descriptor, false);
}
- const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
- if (!return_type.IsLowHalf()) {
- work_line_->SetResultRegisterType(return_type);
+ if (!return_type->IsLowHalf()) {
+ work_line_->SetResultRegisterType(*return_type);
} else {
- work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+ work_line_->SetResultRegisterTypeWide(*return_type, return_type->HighHalf(&reg_types_));
}
just_set_result = true;
break;
@@ -2159,7 +2188,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
if (called_method == NULL) {
uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx);
- is_constructor = StringPiece(dex_file_->GetMethodName(method_id)) == "<init>";
+ is_constructor = strcmp("<init>", dex_file_->StringDataByIdx(method_id.name_idx_)) == 0;
uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_;
return_type_descriptor = dex_file_->StringByTypeIdx(return_type_idx);
} else {
@@ -2892,7 +2921,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() {
}
mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_method_idx,
- MethodType method_type) {
+ MethodType method_type) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
const RegType& klass_type = ResolveClassAndCheckAccess(method_id.class_idx_);
if (klass_type.IsConflict()) {
@@ -2909,7 +2938,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth
mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx);
if (res_method == NULL) {
const char* name = dex_file_->GetMethodName(method_id);
- std::string signature(dex_file_->CreateMethodSignature(method_id.proto_idx_, NULL));
+ const Signature signature = dex_file_->GetMethodSignature(method_id);
if (method_type == METHOD_DIRECT || method_type == METHOD_STATIC) {
res_method = klass->FindDirectMethod(name, signature);
@@ -3114,6 +3143,8 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst
const RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range);
if (actual_arg_type.IsConflict()) { // GetInvocationThis failed.
return NULL;
+ } else if (actual_arg_type.IsZero()) { // Invoke on "null" instance: we can't go further.
+ return NULL;
}
mirror::Class* this_class = NULL;
if (!actual_arg_type.IsUnresolvedTypes()) {
@@ -3507,22 +3538,26 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty
const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
field = GetInstanceField(object_type, field_idx);
}
- const char* descriptor;
- mirror::ClassLoader* loader;
+ const RegType* field_type = nullptr;
if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
+ FieldHelper fh(field);
+ mirror::Class* field_type_class = fh.GetType(false);
+ if (field_type_class != nullptr) {
+ field_type = &reg_types_.FromClass(fh.GetTypeDescriptor(), field_type_class,
+ field_type_class->CannotBeAssignedFromOtherTypes());
+ }
+ }
+ if (field_type == nullptr) {
const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = class_loader_;
+ const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+ mirror::ClassLoader* loader = class_loader_;
+ field_type = &reg_types_.FromDescriptor(loader, descriptor, false);
}
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
if (is_primitive) {
- if (field_type.Equals(insn_type) ||
- (field_type.IsFloat() && insn_type.IsInteger()) ||
- (field_type.IsDouble() && insn_type.IsLong())) {
+ if (field_type->Equals(insn_type) ||
+ (field_type->IsFloat() && insn_type.IsInteger()) ||
+ (field_type->IsDouble() && insn_type.IsLong())) {
// expected that read is of the correct primitive type or that int reads are reading
// floats or long reads are reading doubles
} else {
@@ -3535,7 +3570,7 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty
return;
}
} else {
- if (!insn_type.IsAssignableFrom(field_type)) {
+ if (!insn_type.IsAssignableFrom(*field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
<< " to be compatible with type '" << insn_type
<< "' but found type '" << field_type
@@ -3544,10 +3579,10 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty
return;
}
}
- if (!field_type.IsLowHalf()) {
- work_line_->SetRegisterType(vregA, field_type);
+ if (!field_type->IsLowHalf()) {
+ work_line_->SetRegisterType(vregA, *field_type);
} else {
- work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(&reg_types_));
+ work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
}
}
@@ -3561,36 +3596,38 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty
const RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c());
field = GetInstanceField(object_type, field_idx);
}
- const char* descriptor;
- mirror::ClassLoader* loader;
- if (field != NULL) {
- descriptor = FieldHelper(field).GetTypeDescriptor();
- loader = field->GetDeclaringClass()->GetClassLoader();
- } else {
- const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
- descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
- loader = class_loader_;
- }
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ const RegType* field_type = nullptr;
if (field != NULL) {
if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) {
Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field)
<< " from other class " << GetDeclaringClass();
return;
}
+ FieldHelper fh(field);
+ mirror::Class* field_type_class = fh.GetType(false);
+ if (field_type_class != nullptr) {
+ field_type = &reg_types_.FromClass(fh.GetTypeDescriptor(), field_type_class,
+ field_type_class->CannotBeAssignedFromOtherTypes());
+ }
+ }
+ if (field_type == nullptr) {
+ const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx);
+ const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id);
+ mirror::ClassLoader* loader = class_loader_;
+ field_type = &reg_types_.FromDescriptor(loader, descriptor, false);
}
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
if (is_primitive) {
- VerifyPrimitivePut(field_type, insn_type, vregA);
+ VerifyPrimitivePut(*field_type, insn_type, vregA);
} else {
- if (!insn_type.IsAssignableFrom(field_type)) {
+ if (!insn_type.IsAssignableFrom(*field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
<< " to be compatible with type '" << insn_type
<< "' but found type '" << field_type
<< "' in put-object";
return;
}
- work_line_->VerifyRegisterType(vregA, field_type);
+ work_line_->VerifyRegisterType(vregA, *field_type);
}
}
@@ -3658,14 +3695,21 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name();
return;
}
- const char* descriptor = FieldHelper(field).GetTypeDescriptor();
- mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader();
- const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false);
+ FieldHelper fh(field);
+ mirror::Class* field_type_class = fh.GetType(false);
+ const RegType* field_type;
+ if (field_type_class != nullptr) {
+ field_type = &reg_types_.FromClass(fh.GetTypeDescriptor(), field_type_class,
+ field_type_class->CannotBeAssignedFromOtherTypes());
+ } else {
+ field_type = &reg_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(),
+ fh.GetTypeDescriptor(), false);
+ }
const uint32_t vregA = inst->VRegA_22c();
if (is_primitive) {
- if (field_type.Equals(insn_type) ||
- (field_type.IsFloat() && insn_type.IsIntegralTypes()) ||
- (field_type.IsDouble() && insn_type.IsLongTypes())) {
+ if (field_type->Equals(insn_type) ||
+ (field_type->IsFloat() && insn_type.IsIntegralTypes()) ||
+ (field_type->IsDouble() && insn_type.IsLongTypes())) {
// expected that read is of the correct primitive type or that int reads are reading
// floats or long reads are reading doubles
} else {
@@ -3678,7 +3722,7 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins
return;
}
} else {
- if (!insn_type.IsAssignableFrom(field_type)) {
+ if (!insn_type.IsAssignableFrom(*field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
<< " to be compatible with type '" << insn_type
<< "' but found type '" << field_type
@@ -3687,10 +3731,10 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins
return;
}
}
- if (!field_type.IsLowHalf()) {
- work_line_->SetRegisterType(vregA, field_type);
+ if (!field_type->IsLowHalf()) {
+ work_line_->SetRegisterType(vregA, *field_type);
} else {
- work_line_->SetRegisterTypeWide(vregA, field_type, field_type.HighHalf(&reg_types_));
+ work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(&reg_types_));
}
}
@@ -3804,7 +3848,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, const RegisterLine* mer
}
} else {
UniquePtr<RegisterLine> copy(gDebugVerify ?
- new RegisterLine(target_line->NumRegs(), this) :
+ RegisterLine::Create(target_line->NumRegs(), this) :
NULL);
if (gDebugVerify) {
copy->CopyFromLine(target_line);
@@ -3832,11 +3876,28 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() {
}
const RegType& MethodVerifier::GetMethodReturnType() {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
- const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
- uint16_t return_type_idx = proto_id.return_type_idx_;
- const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
- return reg_types_.FromDescriptor(class_loader_, descriptor, false);
+ if (return_type_ == nullptr) {
+ if (mirror_method_ != NULL) {
+ MethodHelper mh(mirror_method_);
+ mirror::Class* return_type_class = mh.GetReturnType();
+ if (return_type_class != nullptr) {
+ return_type_ =&reg_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class,
+ return_type_class->CannotBeAssignedFromOtherTypes());
+ } else {
+ Thread* self = Thread::Current();
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
+ }
+ if (return_type_ == nullptr) {
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
+ const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
+ uint16_t return_type_idx = proto_id.return_type_idx_;
+ const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
+ return_type_ = &reg_types_.FromDescriptor(class_loader_, descriptor, false);
+ }
+ }
+ return *return_type_;
}
const RegType& MethodVerifier::GetDeclaringClass() {
@@ -3892,18 +3953,32 @@ MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() {
code_item_->insns_size_in_code_units_);
for (; inst < end; inst = inst->Next()) {
- if (Instruction::CHECK_CAST != inst->Opcode()) {
- continue;
- }
- uint32_t dex_pc = inst->GetDexPc(code_item_->insns_);
- RegisterLine* line = reg_table_.GetLine(dex_pc);
- const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
- const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
- if (cast_type.IsStrictlyAssignableFrom(reg_type)) {
- if (mscs.get() == NULL) {
- mscs.reset(new MethodSafeCastSet());
+ Instruction::Code code = inst->Opcode();
+ if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
+ uint32_t dex_pc = inst->GetDexPc(code_item_->insns_);
+ RegisterLine* line = reg_table_.GetLine(dex_pc);
+ bool is_safe_cast = false;
+ if (code == Instruction::CHECK_CAST) {
+ const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
+ const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
+ is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
+ } else {
+ const RegType& array_type(line->GetRegisterType(inst->VRegB_23x()));
+ // We only know its safe to assign to an array if the array type is precise. For example,
+ // an Object[] can have any type of object stored in it, but it may also be assigned a
+ // String[] in which case the stores need to be of Strings.
+ if (array_type.IsPreciseReference()) {
+ const RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
+ const RegType& component_type(reg_types_.GetComponentType(array_type, class_loader_));
+ is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
+ }
+ }
+ if (is_safe_cast) {
+ if (mscs.get() == NULL) {
+ mscs.reset(new MethodSafeCastSet());
+ }
+ mscs->insert(dex_pc);
}
- mscs->insert(dex_pc);
}
}
return mscs.release();
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 073a2f76be..57fde1d5d5 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -110,10 +110,8 @@ enum RegisterTrackingMode {
// execution of that instruction.
class PcToRegisterLineTable {
public:
- PcToRegisterLineTable() {}
- ~PcToRegisterLineTable() {
- STLDeleteValues(&pc_to_register_line_);
- }
+ PcToRegisterLineTable() : size_(0) {}
+ ~PcToRegisterLineTable();
// Initialize the RegisterTable. Every instruction address can have a different set of information
// about what's in which register, but for verification purposes we only need to store it at
@@ -122,17 +120,13 @@ class PcToRegisterLineTable {
uint16_t registers_size, MethodVerifier* verifier);
RegisterLine* GetLine(size_t idx) {
- auto result = pc_to_register_line_.find(idx);
- if (result == pc_to_register_line_.end()) {
- return NULL;
- } else {
- return result->second;
- }
+ DCHECK_LT(idx, size_);
+ return register_lines_[idx];
}
private:
- typedef SafeMap<int32_t, RegisterLine*> Table;
- Table pc_to_register_line_;
+ UniquePtr<RegisterLine*[]> register_lines_;
+ size_t size_;
};
// The verifier
@@ -688,6 +682,7 @@ class MethodVerifier {
// Its object representation if known.
mirror::ArtMethod* mirror_method_ GUARDED_BY(Locks::mutator_lock_);
const uint32_t method_access_flags_; // Method's access flags.
+ const RegType* return_type_; // Lazily computed return type of the method.
const DexFile* const dex_file_; // The dex file containing the method.
// The dex_cache for the declaring class of the method.
mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_);
@@ -729,10 +724,12 @@ class MethodVerifier {
// running and the verifier is called from the class linker.
const bool allow_soft_failures_;
- // Indicates if the method being verified contains at least one check-cast instruction.
+ // Indicates the method being verified contains at least one check-cast or aput-object
+ // instruction. Aput-object operations implicitly check for array-store exceptions, similar to
+ // check-cast.
bool has_check_casts_;
- // Indicates if the method being verified contains at least one invoke-virtual/range
+ // Indicates the method being verified contains at least one invoke-virtual/range
// or invoke-interface/range.
bool has_virtual_or_interface_invokes_;
};
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 25f840cc56..50d1583bbb 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -99,7 +99,7 @@ std::string PreciseConstType::Dump() const {
}
std::string BooleanType::Dump() const {
- return "boolean";
+ return "Boolean";
}
std::string ConflictType::Dump() const {
@@ -111,7 +111,7 @@ std::string ByteType::Dump() const {
}
std::string ShortType::Dump() const {
- return "short";
+ return "Short";
}
std::string CharType::Dump() const {
@@ -119,15 +119,15 @@ std::string CharType::Dump() const {
}
std::string FloatType::Dump() const {
- return "float";
+ return "Float";
}
std::string LongLoType::Dump() const {
- return "long (Low Half)";
+ return "Long (Low Half)";
}
std::string LongHiType::Dump() const {
- return "long (High Half)";
+ return "Long (High Half)";
}
std::string DoubleLoType::Dump() const {
@@ -461,7 +461,6 @@ std::string ImpreciseConstType::Dump() const {
std::stringstream result;
uint32_t val = ConstantValue();
if (val == 0) {
- CHECK(IsPreciseConstant());
result << "Zero/null";
} else {
result << "Imprecise ";
@@ -762,11 +761,6 @@ bool RegType::IsStrictlyAssignableFrom(const RegType& src) const {
return AssignableFrom(*this, src, true);
}
-int32_t ConstantType::ConstantValue() const {
- DCHECK(IsConstantTypes());
- return constant_;
-}
-
int32_t ConstantType::ConstantValueLo() const {
DCHECK(IsConstantLo());
return constant_;
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index 865ba20d44..f3717330eb 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -574,9 +574,12 @@ class ConstantType : public RegType {
// If this is a 32-bit constant, what is the value? This value may be imprecise in which case
// the value represents part of the integer range of values that may be held in the register.
- virtual int32_t ConstantValue() const;
- virtual int32_t ConstantValueLo() const;
- virtual int32_t ConstantValueHi() const;
+ int32_t ConstantValue() const {
+ DCHECK(IsConstantTypes());
+ return constant_;
+ }
+ int32_t ConstantValueLo() const;
+ int32_t ConstantValueHi() const;
bool IsZero() const {
return IsPreciseConstant() && ConstantValue() == 0;
diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h
index 295e27198d..fc9e5c98f7 100644
--- a/runtime/verifier/reg_type_cache-inl.h
+++ b/runtime/verifier/reg_type_cache-inl.h
@@ -23,17 +23,6 @@
namespace art {
namespace verifier {
-template <class Type>
-Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
- mirror::Class* klass = NULL;
- // Try loading the class from linker.
- if (!descriptor.empty()) {
- klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
- }
- Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
- RegTypeCache::primitive_count_++;
- return entry;
-}
inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const {
DCHECK_LT(id, entries_.size());
@@ -41,6 +30,16 @@ inline const art::verifier::RegType& RegTypeCache::GetFromId(uint16_t id) const
DCHECK(result != NULL);
return *result;
}
+
+inline const ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
+ // We only expect 0 to be a precise constant.
+ DCHECK(value != 0 || precise);
+ if (precise && (value >= kMinSmallConstant) && (value <= kMaxSmallConstant)) {
+ return *small_precise_constants_[value - kMinSmallConstant];
+ }
+ return FromCat1NonSmallConstant(value, precise);
+}
+
} // namespace verifier
} // namespace art
#endif // ART_RUNTIME_VERIFIER_REG_TYPE_CACHE_INL_H_
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index 2c18132c0b..446dd0080c 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -26,8 +26,8 @@ namespace art {
namespace verifier {
bool RegTypeCache::primitive_initialized_ = false;
-uint16_t RegTypeCache::primitive_start_ = 0;
uint16_t RegTypeCache::primitive_count_ = 0;
+PreciseConstType* RegTypeCache::small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
static bool MatchingPrecisionForClass(RegType* entry, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -44,7 +44,7 @@ static bool MatchingPrecisionForClass(RegType* entry, bool precise)
}
}
-void RegTypeCache::FillPrimitiveTypes() {
+void RegTypeCache::FillPrimitiveAndSmallConstantTypes() {
entries_.push_back(UndefinedType::GetInstance());
entries_.push_back(ConflictType::GetInstance());
entries_.push_back(BooleanType::GetInstance());
@@ -57,6 +57,11 @@ void RegTypeCache::FillPrimitiveTypes() {
entries_.push_back(FloatType::GetInstance());
entries_.push_back(DoubleLoType::GetInstance());
entries_.push_back(DoubleHiType::GetInstance());
+ for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
+ int32_t i = value - kMinSmallConstant;
+ DCHECK_EQ(entries_.size(), small_precise_constants_[i]->GetId());
+ entries_.push_back(small_precise_constants_[i]);
+ }
DCHECK_EQ(entries_.size(), primitive_count_);
}
@@ -205,6 +210,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descr
}
const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) {
+ DCHECK(klass != nullptr);
if (klass->IsPrimitive()) {
// Note: precise isn't used for primitive classes. A char is assignable to an int. All
// primitive classes are final.
@@ -232,12 +238,12 @@ const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* kl
RegTypeCache::~RegTypeCache() {
CHECK_LE(primitive_count_, entries_.size());
// Delete only the non primitive types.
- if (entries_.size() == kNumPrimitives) {
- // All entries are primitive, nothing to delete.
+ if (entries_.size() == kNumPrimitivesAndSmallConstants) {
+ // All entries are from the global pool, nothing to delete.
return;
}
std::vector<RegType*>::iterator non_primitive_begin = entries_.begin();
- std::advance(non_primitive_begin, kNumPrimitives);
+ std::advance(non_primitive_begin, kNumPrimitivesAndSmallConstants);
STLDeleteContainerPointers(non_primitive_begin, entries_.end());
}
@@ -255,12 +261,29 @@ void RegTypeCache::ShutDown() {
FloatType::Destroy();
DoubleLoType::Destroy();
DoubleHiType::Destroy();
+ for (uint16_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
+ PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant];
+ delete type;
+ }
+
RegTypeCache::primitive_initialized_ = false;
RegTypeCache::primitive_count_ = 0;
}
}
-void RegTypeCache::CreatePrimitiveTypes() {
+template <class Type>
+Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) {
+ mirror::Class* klass = NULL;
+ // Try loading the class from linker.
+ if (!descriptor.empty()) {
+ klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(descriptor.c_str());
+ }
+ Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_);
+ RegTypeCache::primitive_count_++;
+ return entry;
+}
+
+void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() {
CreatePrimitiveTypeInstance<UndefinedType>("");
CreatePrimitiveTypeInstance<ConflictType>("");
CreatePrimitiveTypeInstance<BooleanType>("Z");
@@ -273,6 +296,11 @@ void RegTypeCache::CreatePrimitiveTypes() {
CreatePrimitiveTypeInstance<FloatType>("F");
CreatePrimitiveTypeInstance<DoubleLoType>("D");
CreatePrimitiveTypeInstance<DoubleHiType>("D");
+ for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) {
+ PreciseConstType* type = new PreciseConstType(value, primitive_count_);
+ small_precise_constants_[value - kMinSmallConstant] = type;
+ primitive_count_++;
+ }
}
const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) {
@@ -331,29 +359,28 @@ const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) {
return *entry;
}
-const RegType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
- RegType* entry = NULL;
- RegType* cur_entry = NULL;
+const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32_t allocation_pc) {
+ UninitializedType* entry = NULL;
const std::string& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- cur_entry = entries_[i];
+ RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedAndUninitializedReference() &&
down_cast<UnresolvedUninitializedRefType*>(cur_entry)->GetAllocationPc() == allocation_pc &&
(cur_entry->GetDescriptor() == descriptor)) {
- return *cur_entry;
+ return *down_cast<UnresolvedUninitializedRefType*>(cur_entry);
}
}
entry = new UnresolvedUninitializedRefType(descriptor, allocation_pc, entries_.size());
} else {
mirror::Class* klass = type.GetClass();
for (size_t i = primitive_count_; i < entries_.size(); i++) {
- cur_entry = entries_[i];
+ RegType* cur_entry = entries_[i];
if (cur_entry->IsUninitializedReference() &&
down_cast<UninitializedReferenceType*>(cur_entry)
->GetAllocationPc() == allocation_pc &&
cur_entry->GetClass() == klass) {
- return *cur_entry;
+ return *down_cast<UninitializedReferenceType*>(cur_entry);
}
}
entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size());
@@ -404,27 +431,33 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) {
return *entry;
}
-const RegType& RegTypeCache::ByteConstant() {
- return FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+const ImpreciseConstType& RegTypeCache::ByteConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
}
-const RegType& RegTypeCache::ShortConstant() {
- return FromCat1Const(std::numeric_limits<jshort>::min(), false);
+const ImpreciseConstType& RegTypeCache::ShortConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jshort>::min(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
}
-const RegType& RegTypeCache::IntConstant() {
- return FromCat1Const(std::numeric_limits<jint>::max(), false);
+const ImpreciseConstType& RegTypeCache::IntConstant() {
+ const ConstantType& result = FromCat1Const(std::numeric_limits<jint>::max(), false);
+ DCHECK(result.IsImpreciseConstant());
+ return *down_cast<const ImpreciseConstType*>(&result);
}
-const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
- RegType* entry;
+const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
+ UninitializedType* entry;
const std::string& descriptor(type.GetDescriptor());
if (type.IsUnresolvedTypes()) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedAndUninitializedThisReference() &&
cur_entry->GetDescriptor() == descriptor) {
- return *cur_entry;
+ return *down_cast<UninitializedType*>(cur_entry);
}
}
entry = new UnresolvedUninitializedThisRefType(descriptor, entries_.size());
@@ -433,7 +466,7 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry->IsUninitializedThisReference() && cur_entry->GetClass() == klass) {
- return *cur_entry;
+ return *down_cast<UninitializedType*>(cur_entry);
}
}
entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size());
@@ -442,16 +475,16 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) {
return *entry;
}
-const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool precise) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry->klass_ == NULL && cur_entry->IsConstant() &&
cur_entry->IsPreciseConstant() == precise &&
(down_cast<ConstantType*>(cur_entry))->ConstantValue() == value) {
- return *cur_entry;
+ return *down_cast<ConstantType*>(cur_entry);
}
}
- RegType* entry;
+ ConstantType* entry;
if (precise) {
entry = new PreciseConstType(value, entries_.size());
} else {
@@ -461,15 +494,15 @@ const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
return *entry;
}
-const RegType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry->IsConstantLo() && (cur_entry->IsPrecise() == precise) &&
(down_cast<ConstantType*>(cur_entry))->ConstantValueLo() == value) {
- return *cur_entry;
+ return *down_cast<ConstantType*>(cur_entry);
}
}
- RegType* entry;
+ ConstantType* entry;
if (precise) {
entry = new PreciseConstLoType(value, entries_.size());
} else {
@@ -479,15 +512,15 @@ const RegType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
return *entry;
}
-const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
+const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
for (size_t i = primitive_count_; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry->IsConstantHi() && (cur_entry->IsPrecise() == precise) &&
(down_cast<ConstantType*>(cur_entry))->ConstantValueHi() == value) {
- return *cur_entry;
+ return *down_cast<ConstantType*>(cur_entry);
}
}
- RegType* entry;
+ ConstantType* entry;
if (precise) {
entry = new PreciseConstHiType(value, entries_.size());
} else {
@@ -498,8 +531,9 @@ const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
}
const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::ClassLoader* loader) {
- CHECK(array.IsArrayTypes());
- if (array.IsUnresolvedTypes()) {
+ if (!array.IsArrayTypes()) {
+ return Conflict();
+ } else if (array.IsUnresolvedTypes()) {
const std::string& descriptor(array.GetDescriptor());
const std::string component(descriptor.substr(1, descriptor.size() - 1));
return FromDescriptor(loader, component.c_str(), false);
diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h
index 77f58934da..a9f8bff784 100644
--- a/runtime/verifier/reg_type_cache.h
+++ b/runtime/verifier/reg_type_cache.h
@@ -35,19 +35,18 @@ namespace verifier {
class RegType;
-const size_t kNumPrimitives = 12;
class RegTypeCache {
public:
explicit RegTypeCache(bool can_load_classes) : can_load_classes_(can_load_classes) {
entries_.reserve(64);
- FillPrimitiveTypes();
+ FillPrimitiveAndSmallConstantTypes();
}
~RegTypeCache();
static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (!RegTypeCache::primitive_initialized_) {
CHECK_EQ(RegTypeCache::primitive_count_, 0);
- CreatePrimitiveTypes();
- CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitives);
+ CreatePrimitiveAndSmallConstantTypes();
+ CHECK_EQ(RegTypeCache::primitive_count_, kNumPrimitivesAndSmallConstants);
RegTypeCache::primitive_initialized_ = true;
}
}
@@ -55,17 +54,13 @@ class RegTypeCache {
const art::verifier::RegType& GetFromId(uint16_t id) const;
const RegType& From(mirror::ClassLoader* loader, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- template <class Type>
- static Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void FillPrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat1Const(int32_t value, bool precise)
+ const ConstantType& FromCat1Const(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat2ConstLo(int32_t value, bool precise)
+ const ConstantType& FromCat2ConstLo(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat2ConstHi(int32_t value, bool precise)
+ const ConstantType& FromCat2ConstHi(int32_t value, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromDescriptor(mirror::ClassLoader* loader, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -129,34 +124,56 @@ class RegTypeCache {
const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return From(NULL, "Ljava/lang/Object;", precise);
}
- const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc)
+ const UninitializedType& Uninitialized(const RegType& type, uint32_t allocation_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Create an uninitialized 'this' argument for the given type.
- const RegType& UninitializedThisArgument(const RegType& type)
+ const UninitializedType& UninitializedThisArgument(const RegType& type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromUninitialized(const RegType& uninit_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ImpreciseConstType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& GetComponentType(const RegType& array, mirror::ClassLoader* loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& RegTypeFromPrimitiveType(Primitive::Type) const;
private:
- std::vector<RegType*> entries_;
- static bool primitive_initialized_;
- static uint16_t primitive_start_;
- static uint16_t primitive_count_;
- static void CreatePrimitiveTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Whether or not we're allowed to load classes.
- const bool can_load_classes_;
+ void FillPrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
mirror::Class* ResolveClass(const char* descriptor, mirror::ClassLoader* loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void ClearException();
bool MatchDescriptor(size_t idx, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ template <class Type>
+ static Type* CreatePrimitiveTypeInstance(const std::string& descriptor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void CreatePrimitiveAndSmallConstantTypes() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // The actual storage for the RegTypes.
+ std::vector<RegType*> entries_;
+
+ // A quick look up for popular small constants.
+ static constexpr int32_t kMinSmallConstant = -1;
+ static constexpr int32_t kMaxSmallConstant = 4;
+ static PreciseConstType* small_precise_constants_[kMaxSmallConstant - kMinSmallConstant + 1];
+
+ static constexpr size_t kNumPrimitivesAndSmallConstants =
+ 12 + (kMaxSmallConstant - kMinSmallConstant + 1);
+
+ // Have the well known global primitives been created?
+ static bool primitive_initialized_;
+
+ // Number of well known primitives that will be copied into a RegTypeCache upon construction.
+ static uint16_t primitive_count_;
+
+ // Whether or not we're allowed to load classes.
+ const bool can_load_classes_;
+
DISALLOW_COPY_AND_ASSIGN(RegTypeCache);
};
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index a615cc1273..1a41657264 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -456,8 +456,7 @@ bool RegisterLine::VerifyMonitorStackEmpty() const {
bool RegisterLine::MergeRegisters(const RegisterLine* incoming_line) {
bool changed = false;
- CHECK(NULL != incoming_line);
- CHECK(NULL != line_.get());
+ DCHECK(incoming_line != nullptr);
for (size_t idx = 0; idx < num_regs_; idx++) {
if (line_[idx] != incoming_line->line_[idx]) {
const RegType& incoming_reg_type = incoming_line->GetRegisterType(idx);
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index f19dccac17..8b2dadb119 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -17,7 +17,6 @@
#ifndef ART_RUNTIME_VERIFIER_REGISTER_LINE_H_
#define ART_RUNTIME_VERIFIER_REGISTER_LINE_H_
-#include <deque>
#include <vector>
#include "dex_instruction.h"
@@ -51,12 +50,10 @@ enum TypeCategory {
// stack of entered monitors (identified by code unit offset).
class RegisterLine {
public:
- RegisterLine(size_t num_regs, MethodVerifier* verifier)
- : line_(new uint16_t[num_regs]),
- verifier_(verifier),
- num_regs_(num_regs) {
- memset(line_.get(), 0, num_regs_ * sizeof(uint16_t));
- SetResultTypeToUnknown();
+ static RegisterLine* Create(size_t num_regs, MethodVerifier* verifier) {
+ uint8_t* memory = new uint8_t[sizeof(RegisterLine) + (num_regs * sizeof(uint16_t))];
+ RegisterLine* rl = new (memory) RegisterLine(num_regs, verifier);
+ return rl;
}
// Implement category-1 "move" instructions. Copy a 32-bit value from "vsrc" to "vdst".
@@ -108,7 +105,7 @@ class RegisterLine {
void CopyFromLine(const RegisterLine* src) {
DCHECK_EQ(num_regs_, src->num_regs_);
- memcpy(line_.get(), src->line_.get(), num_regs_ * sizeof(uint16_t));
+ memcpy(&line_, &src->line_, num_regs_ * sizeof(uint16_t));
monitors_ = src->monitors_;
reg_to_lock_depths_ = src->reg_to_lock_depths_;
}
@@ -116,7 +113,7 @@ class RegisterLine {
std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void FillWithGarbage() {
- memset(line_.get(), 0xf1, num_regs_ * sizeof(uint16_t));
+ memset(&line_, 0xf1, num_regs_ * sizeof(uint16_t));
while (!monitors_.empty()) {
monitors_.pop_back();
}
@@ -161,7 +158,7 @@ class RegisterLine {
int CompareLine(const RegisterLine* line2) const {
DCHECK(monitors_ == line2->monitors_);
// TODO: DCHECK(reg_to_lock_depths_ == line2->reg_to_lock_depths_);
- return memcmp(line_.get(), line2->line_.get(), num_regs_ * sizeof(uint16_t));
+ return memcmp(&line_, &line2->line_, num_regs_ * sizeof(uint16_t));
}
size_t NumRegs() const {
@@ -339,23 +336,30 @@ class RegisterLine {
reg_to_lock_depths_.erase(reg);
}
+ RegisterLine(size_t num_regs, MethodVerifier* verifier)
+ : verifier_(verifier),
+ num_regs_(num_regs) {
+ memset(&line_, 0, num_regs_ * sizeof(uint16_t));
+ SetResultTypeToUnknown();
+ }
+
// Storage for the result register's type, valid after an invocation
uint16_t result_[2];
- // An array of RegType Ids associated with each dex register
- UniquePtr<uint16_t[]> line_;
-
// Back link to the verifier
MethodVerifier* verifier_;
// Length of reg_types_
const uint32_t num_regs_;
// A stack of monitor enter locations
- std::deque<uint32_t> monitors_;
+ std::vector<uint32_t> monitors_;
// A map from register to a bit vector of indices into the monitors_ stack. As we pop the monitor
// stack we verify that monitor-enter/exit are correctly nested. That is, if there was a
// monitor-enter on v5 and then on v6, we expect the monitor-exit to be on v6 then on v5
SafeMap<uint32_t, uint32_t> reg_to_lock_depths_;
+
+ // An array of RegType Ids associated with each dex register.
+ uint16_t line_[0];
};
std::ostream& operator<<(std::ostream& os, const RegisterLine& rhs);
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index 8e09e783ed..db273ec7bf 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -19,10 +19,12 @@
#include <vector>
#include <fcntl.h>
+#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "UniquePtr.h"
@@ -247,35 +249,38 @@ static bool InflateToMemory(uint8_t* begin, size_t size,
return true;
}
-bool ZipEntry::ExtractToFile(File& file) {
+bool ZipEntry::ExtractToFile(File& file, std::string* error_msg) {
uint32_t length = GetUncompressedLength();
int result = TEMP_FAILURE_RETRY(ftruncate(file.Fd(), length));
if (result == -1) {
- PLOG(WARNING) << "Zip: failed to ftruncate " << file.GetPath() << " to length " << length;
+ *error_msg = StringPrintf("Zip: failed to ftruncate '%s' to length %ud", file.GetPath().c_str(),
+ length);
return false;
}
- UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0));
+ UniquePtr<MemMap> map(MemMap::MapFile(length, PROT_READ | PROT_WRITE, MAP_SHARED, file.Fd(), 0,
+ file.GetPath().c_str(), error_msg));
if (map.get() == NULL) {
- LOG(WARNING) << "Zip: failed to mmap space for " << file.GetPath();
+ *error_msg = StringPrintf("Zip: failed to mmap space for '%s': %s", file.GetPath().c_str(),
+ error_msg->c_str());
return false;
}
- return ExtractToMemory(map->Begin(), map->Size());
+ return ExtractToMemory(map->Begin(), map->Size(), error_msg);
}
-bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size) {
+bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg) {
// If size is zero, data offset will be meaningless, so bail out early.
if (size == 0) {
return true;
}
off64_t data_offset = GetDataOffset();
if (data_offset == -1) {
- LOG(WARNING) << "Zip: data_offset=" << data_offset;
+ *error_msg = StringPrintf("Zip: data_offset=%lld", data_offset);
return false;
}
if (lseek64(zip_archive_->fd_, data_offset, SEEK_SET) != data_offset) {
- PLOG(WARNING) << "Zip: lseek to data at " << data_offset << " failed";
+ *error_msg = StringPrintf("Zip: lseek to data at %lld failed", data_offset);
return false;
}
@@ -288,25 +293,25 @@ bool ZipEntry::ExtractToMemory(uint8_t* begin, size_t size) {
return InflateToMemory(begin, size, zip_archive_->fd_,
GetUncompressedLength(), GetCompressedLength());
default:
- LOG(WARNING) << "Zip: unknown compression method " << std::hex << GetCompressionMethod();
+ *error_msg = StringPrintf("Zip: unknown compression method 0x%x", GetCompressionMethod());
return false;
}
}
-MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename) {
+MemMap* ZipEntry::ExtractToMemMap(const char* entry_filename, std::string* error_msg) {
std::string name(entry_filename);
name += " extracted in memory from ";
name += entry_filename;
UniquePtr<MemMap> map(MemMap::MapAnonymous(name.c_str(),
NULL,
GetUncompressedLength(),
- PROT_READ | PROT_WRITE));
- if (map.get() == NULL) {
- LOG(ERROR) << "Zip: mmap for '" << entry_filename << "' failed";
+ PROT_READ | PROT_WRITE, error_msg));
+ if (map.get() == nullptr) {
+ DCHECK(!error_msg->empty());
return NULL;
}
- bool success = ExtractToMemory(map->Begin(), map->Size());
+ bool success = ExtractToMemory(map->Begin(), map->Size(), error_msg);
if (!success) {
LOG(ERROR) << "Zip: Failed to extract '" << entry_filename << "' to memory";
return NULL;
@@ -329,27 +334,25 @@ static void SetCloseOnExec(int fd) {
}
}
-ZipArchive* ZipArchive::Open(const std::string& filename) {
- DCHECK(!filename.empty());
- int fd = open(filename.c_str(), O_RDONLY, 0);
+ZipArchive* ZipArchive::Open(const char* filename, std::string* error_msg) {
+ DCHECK(filename != nullptr);
+ int fd = open(filename, O_RDONLY, 0);
if (fd == -1) {
- PLOG(WARNING) << "Unable to open '" << filename << "'";
+ *error_msg = StringPrintf("Zip: unable to open '%s': %s", filename, strerror(errno));
return NULL;
}
- return OpenFromFd(fd);
+ return OpenFromFd(fd, filename, error_msg);
}
-ZipArchive* ZipArchive::OpenFromFd(int fd) {
+ZipArchive* ZipArchive::OpenFromFd(int fd, const char* filename, std::string* error_msg) {
SetCloseOnExec(fd);
- UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd));
- if (zip_archive.get() == NULL) {
- return NULL;
- }
- if (!zip_archive->MapCentralDirectory()) {
+ UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd, filename));
+ CHECK(zip_archive.get() != nullptr);
+ if (!zip_archive->MapCentralDirectory(error_msg)) {
zip_archive->Close();
return NULL;
}
- if (!zip_archive->Parse()) {
+ if (!zip_archive->Parse(error_msg)) {
zip_archive->Close();
return NULL;
}
@@ -374,19 +377,28 @@ void ZipArchive::Close() {
dir_offset_ = 0;
}
+std::string ZipArchive::ErrorStringPrintf(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ std::string result(StringPrintf("Zip '%s' : ", filename_.c_str()));
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
// Find the zip Central Directory and memory-map it.
//
// On success, returns true after populating fields from the EOCD area:
// num_entries_
// dir_offset_
// dir_map_
-bool ZipArchive::MapCentralDirectory() {
+bool ZipArchive::MapCentralDirectory(std::string* error_msg) {
/*
* Get and test file length.
*/
off64_t file_length = lseek64(fd_, 0, SEEK_END);
if (file_length < kEOCDLen) {
- LOG(WARNING) << "Zip: length " << file_length << " is too small to be zip";
+ *error_msg = ErrorStringPrintf("length %lld is too small to be zip", file_length);
return false;
}
@@ -396,27 +408,26 @@ bool ZipArchive::MapCentralDirectory() {
}
UniquePtr<uint8_t[]> scan_buf(new uint8_t[read_amount]);
- if (scan_buf.get() == NULL) {
- return false;
- }
+ CHECK(scan_buf.get() != nullptr);
/*
* Make sure this is a Zip archive.
*/
if (lseek64(fd_, 0, SEEK_SET) != 0) {
- PLOG(WARNING) << "seek to start failed: ";
+ *error_msg = ErrorStringPrintf("seek to start failed: %s", strerror(errno));
return false;
}
ssize_t actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), sizeof(int32_t)));
if (actual != static_cast<ssize_t>(sizeof(int32_t))) {
- PLOG(INFO) << "couldn't read first signature from zip archive: ";
+ *error_msg = ErrorStringPrintf("couldn\'t read first signature from zip archive: %s",
+ strerror(errno));
return false;
}
unsigned int header = Le32ToHost(scan_buf.get());
if (header != kLFHSignature) {
- LOG(VERBOSE) << "Not a Zip archive (found " << std::hex << header << ")";
+ *error_msg = ErrorStringPrintf("not a zip archive (found 0x%x)", header);
return false;
}
@@ -433,12 +444,13 @@ bool ZipArchive::MapCentralDirectory() {
off64_t search_start = file_length - read_amount;
if (lseek64(fd_, search_start, SEEK_SET) != search_start) {
- PLOG(WARNING) << "Zip: seek " << search_start << " failed";
+ *error_msg = ErrorStringPrintf("seek %lld failed: %s", search_start, strerror(errno));
return false;
}
actual = TEMP_FAILURE_RETRY(read(fd_, scan_buf.get(), read_amount));
if (actual != static_cast<ssize_t>(read_amount)) {
- PLOG(WARNING) << "Zip: read " << actual << ", expected " << read_amount << ". failed";
+ *error_msg = ErrorStringPrintf("read %lld, expected %zd. %s", search_start, read_amount,
+ strerror(errno));
return false;
}
@@ -454,14 +466,14 @@ bool ZipArchive::MapCentralDirectory() {
}
}
if (i < 0) {
- LOG(WARNING) << "Zip: EOCD not found, not a zip file";
+ *error_msg = ErrorStringPrintf("EOCD not found, not a zip file");
return false;
}
off64_t eocd_offset = search_start + i;
const byte* eocd_ptr = scan_buf.get() + i;
- DCHECK(eocd_offset < file_length);
+ CHECK(eocd_offset < file_length);
// Grab the CD offset and size, and the number of entries in the
// archive. Verify that they look reasonable.
@@ -474,29 +486,28 @@ bool ZipArchive::MapCentralDirectory() {
uint16_t comment_size = Le16ToHost(eocd_ptr + kEOCDCommentSize);
if ((uint64_t) dir_offset + (uint64_t) dir_size > (uint64_t) eocd_offset) {
- LOG(WARNING) << "Zip: bad offsets ("
- << "dir=" << dir_offset << ", "
- << "size=" << dir_size << ", "
- << "eocd=" << eocd_offset << ")";
+ *error_msg = ErrorStringPrintf("bad offsets (dir=%ud, size=%ud, eocd=%lld)",
+ dir_offset, dir_size, eocd_offset);
return false;
}
if (num_entries == 0) {
- LOG(WARNING) << "Zip: empty archive?";
+ *error_msg = ErrorStringPrintf("empty archive?");
return false;
} else if (num_entries != total_num_entries || disk_number != 0 || disk_with_central_dir != 0) {
- LOG(WARNING) << "spanned archives not supported";
+ *error_msg = ErrorStringPrintf("spanned archives not supported");
return false;
}
// Check to see if comment is a sane size
if ((comment_size > (file_length - kEOCDLen))
|| (eocd_offset > (file_length - kEOCDLen) - comment_size)) {
- LOG(WARNING) << "comment size runs off end of file";
+ *error_msg = ErrorStringPrintf("comment size runs off end of file");
return false;
}
// It all looks good. Create a mapping for the CD.
- dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset));
+ dir_map_.reset(MemMap::MapFile(dir_size, PROT_READ, MAP_SHARED, fd_, dir_offset,
+ filename_.c_str(), error_msg));
if (dir_map_.get() == NULL) {
return false;
}
@@ -506,7 +517,7 @@ bool ZipArchive::MapCentralDirectory() {
return true;
}
-bool ZipArchive::Parse() {
+bool ZipArchive::Parse(std::string* error_msg) {
const byte* cd_ptr = dir_map_->Begin();
size_t cd_length = dir_map_->Size();
@@ -515,23 +526,23 @@ bool ZipArchive::Parse() {
const byte* ptr = cd_ptr;
for (int i = 0; i < num_entries_; i++) {
if (Le32ToHost(ptr) != kCDESignature) {
- LOG(WARNING) << "Zip: missed a central dir sig (at " << i << ")";
+ *error_msg = ErrorStringPrintf("missed a central dir sig (at %d)", i);
return false;
}
if (ptr + kCDELen > cd_ptr + cd_length) {
- LOG(WARNING) << "Zip: ran off the end (at " << i << ")";
+ *error_msg = ErrorStringPrintf("ran off the end (at %d)", i);
return false;
}
int64_t local_hdr_offset = Le32ToHost(ptr + kCDELocalOffset);
if (local_hdr_offset >= dir_offset_) {
- LOG(WARNING) << "Zip: bad LFH offset " << local_hdr_offset << " at entry " << i;
+ *error_msg = ErrorStringPrintf("bad LFH offset %lld at entry %d", local_hdr_offset, i);
return false;
}
uint16_t gpbf = Le16ToHost(ptr + kCDEGPBFlags);
if ((gpbf & kGPFUnsupportedMask) != 0) {
- LOG(WARNING) << "Invalid General Purpose Bit Flag: " << gpbf;
+ *error_msg = ErrorStringPrintf("invalid general purpose bit flag %x", gpbf);
return false;
}
@@ -544,16 +555,15 @@ bool ZipArchive::Parse() {
// Check name for NULL characters
if (memchr(name, 0, name_len) != NULL) {
- LOG(WARNING) << "Filename contains NUL byte";
+ *error_msg = ErrorStringPrintf("filename contains NUL byte");
return false;
}
dir_entries_.Put(StringPiece(name, name_len), ptr);
ptr += kCDELen + name_len + extra_len + comment_len;
if (ptr > cd_ptr + cd_length) {
- LOG(WARNING) << "Zip: bad CD advance "
- << "(" << ptr << " vs " << (cd_ptr + cd_length) << ") "
- << "at entry " << i;
+ *error_msg = ErrorStringPrintf("bad CD advance (%p vs %p) at entry %d",
+ ptr, cd_ptr + cd_length, i);
return false;
}
}
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index d9ccba2cce..8ff952baab 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <zlib.h>
+#include <string>
#include "base/logging.h"
#include "base/stringpiece.h"
@@ -36,9 +37,9 @@ class MemMap;
class ZipEntry {
public:
- bool ExtractToFile(File& file);
- bool ExtractToMemory(uint8_t* begin, size_t size);
- MemMap* ExtractToMemMap(const char* entry_filename);
+ bool ExtractToFile(File& file, std::string* error_msg);
+ bool ExtractToMemory(uint8_t* begin, size_t size, std::string* error_msg);
+ MemMap* ExtractToMemMap(const char* entry_filename, std::string* error_msg);
uint32_t GetUncompressedLength();
uint32_t GetCrc32();
@@ -109,8 +110,8 @@ class ZipArchive {
static const int32_t kGPFUnsupportedMask = (kGPFEncryptedFlag);
// return new ZipArchive instance on success, NULL on error.
- static ZipArchive* Open(const std::string& filename);
- static ZipArchive* OpenFromFd(int fd);
+ static ZipArchive* Open(const char* filename, std::string* error_msg);
+ static ZipArchive* OpenFromFd(int fd, const char* filename, std::string* error_msg);
ZipEntry* Find(const char* name) const;
@@ -119,11 +120,14 @@ class ZipArchive {
}
private:
- explicit ZipArchive(int fd) : fd_(fd), num_entries_(0), dir_offset_(0) {}
+ explicit ZipArchive(int fd, const char* filename)
+ : fd_(fd), num_entries_(0), dir_offset_(0), filename_(filename) {}
- bool MapCentralDirectory();
- bool Parse();
+ bool MapCentralDirectory(std::string* error_msg);
+ bool Parse(std::string* error_msg);
void Close();
+ std::string ErrorStringPrintf(const char* fmt, ...)
+ __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
int fd_;
uint16_t num_entries_;
@@ -131,6 +135,8 @@ class ZipArchive {
UniquePtr<MemMap> dir_map_;
typedef SafeMap<StringPiece, const byte*> DirEntries;
DirEntries dir_entries_;
+ // Containing file for error reporting.
+ const std::string filename_;
friend class ZipEntry;
diff --git a/runtime/zip_archive_test.cc b/runtime/zip_archive_test.cc
index 9bdc24ba03..622dc89587 100644
--- a/runtime/zip_archive_test.cc
+++ b/runtime/zip_archive_test.cc
@@ -29,8 +29,10 @@ namespace art {
class ZipArchiveTest : public CommonTest {};
TEST_F(ZipArchiveTest, FindAndExtract) {
- UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName()));
- ASSERT_TRUE(zip_archive.get() != false);
+ std::string error_msg;
+ UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(GetLibCoreDexFileName().c_str(), &error_msg));
+ ASSERT_TRUE(zip_archive.get() != false) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
UniquePtr<ZipEntry> zip_entry(zip_archive->Find("classes.dex"));
ASSERT_TRUE(zip_entry.get() != false);
@@ -38,8 +40,9 @@ TEST_F(ZipArchiveTest, FindAndExtract) {
ASSERT_NE(-1, tmp.GetFd());
UniquePtr<File> file(new File(tmp.GetFd(), tmp.GetFilename()));
ASSERT_TRUE(file.get() != NULL);
- bool success = zip_entry->ExtractToFile(*file);
- ASSERT_TRUE(success);
+ bool success = zip_entry->ExtractToFile(*file, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+ ASSERT_TRUE(error_msg.empty());
file.reset(NULL);
uint32_t computed_crc = crc32(0L, Z_NULL, 0);
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 967f167f37..3d87ebc559 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -35,7 +35,7 @@ z (class java.lang.Character)
62 (class java.lang.Long)
14 (class java.lang.Short)
[public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)]
-[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR]
+[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID]
[void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
diff --git a/test/Android.mk b/test/Android.mk
index da469d75d7..6d3a84a25f 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -23,8 +23,8 @@ include art/build/Android.common.mk
TEST_DEX_DIRECTORIES := \
AbstractMethod \
AllFields \
- CreateMethodSignature \
ExceptionHandle \
+ GetMethodSignature \
Interfaces \
Main \
MyClass \
@@ -44,6 +44,7 @@ TEST_OAT_DIRECTORIES := \
Main \
HelloWorld \
\
+ InterfaceTest \
JniTest \
NativeAllocations \
ParallelGC \
diff --git a/test/CreateMethodSignature/CreateMethodSignature.java b/test/GetMethodSignature/GetMethodSignature.java
index f6cd6ae6fd..c2ba948d60 100644
--- a/test/CreateMethodSignature/CreateMethodSignature.java
+++ b/test/GetMethodSignature/GetMethodSignature.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-class CreateMethodSignature {
+class GetMethodSignature {
Float m1(int a, double b, long c, Object d) { return null; }
- CreateMethodSignature m2(boolean x, short y, char z) { return null; }
+ GetMethodSignature m2(boolean x, short y, char z) { return null; }
}
diff --git a/test/InterfaceTest/InterfaceTest.java b/test/InterfaceTest/InterfaceTest.java
new file mode 100644
index 0000000000..ed18eb3dc7
--- /dev/null
+++ b/test/InterfaceTest/InterfaceTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+import java.util.Map;
+import java.util.HashMap;
+
+class InterfaceTest {
+
+ public static long test_virtual(HashMap map) {
+ Integer intobj = new Integer(0);
+ String s = "asdf";
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < 1000000; i++) {
+ map.put(intobj, s);
+ }
+ long end = System.currentTimeMillis();
+ return (end - start);
+ }
+
+ public static long test_interface(Map map) {
+ Integer intobj = new Integer(0);
+ String s = "asdf";
+ long start = System.currentTimeMillis();
+ for (int i = 0; i < 1000000; i++) {
+ map.put(intobj, s);
+ }
+ long end = System.currentTimeMillis();
+ return (end - start);
+ }
+
+ public static void main(String[] args) {
+ HashMap hashmap = new HashMap();
+ long elapsed = test_virtual(hashmap);
+ System.logI("virtual map put: " + elapsed);
+ hashmap.clear();
+
+ elapsed = test_interface(hashmap);
+ System.logI("interface map put: " + elapsed);
+ }
+}
diff --git a/test/run-test b/test/run-test
index a34cc72b1e..f706110a2c 100755
--- a/test/run-test
+++ b/test/run-test
@@ -309,7 +309,7 @@ elif [ "$build_only" = "yes" ]; then
fi
fi
# Clean up extraneous files that are not used by tests.
- find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$build_output\|$expected\)" | xargs rm -rf
+ find $tmp_dir -mindepth 1 ! -regex ".*/\(.*jar\|$output\|$expected\)" | xargs rm -rf
exit 0
else
"./${build}" >"$build_output" 2>&1