summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--build/Android.common_build.mk3
-rw-r--r--build/Android.common_test.mk4
-rw-r--r--build/Android.gtest.mk12
-rw-r--r--cmdline/cmdline_parser_test.cc7
-rw-r--r--cmdline/cmdline_types.h2
-rw-r--r--compiler/Android.mk4
-rw-r--r--compiler/common_compiler_test.cc11
-rw-r--r--compiler/dex/quick/quick_cfi_test.cc21
-rw-r--r--compiler/dex/quick/x86/quick_assemble_x86_test.cc23
-rw-r--r--compiler/driver/compiled_method_storage_test.cc26
-rw-r--r--compiler/driver/compiler_driver-inl.h8
-rw-r--r--compiler/driver/compiler_driver.cc26
-rw-r--r--compiler/driver/compiler_driver.h26
-rw-r--r--compiler/elf_builder.h298
-rw-r--r--compiler/elf_writer.h9
-rw-r--r--compiler/elf_writer_quick.cc52
-rw-r--r--compiler/image_test.cc31
-rw-r--r--compiler/image_writer.cc550
-rw-r--r--compiler/image_writer.h135
-rw-r--r--compiler/jit/jit_compiler.cc18
-rw-r--r--compiler/linker/arm/relative_patcher_arm_base.cc5
-rw-r--r--compiler/linker/arm/relative_patcher_arm_base.h15
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2.cc6
-rw-r--r--compiler/linker/arm/relative_patcher_thumb2.h12
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.cc6
-rw-r--r--compiler/linker/arm64/relative_patcher_arm64.h15
-rw-r--r--compiler/linker/multi_oat_relative_patcher.cc72
-rw-r--r--compiler/linker/multi_oat_relative_patcher.h146
-rw-r--r--compiler/linker/multi_oat_relative_patcher_test.cc299
-rw-r--r--compiler/linker/relative_patcher.cc3
-rw-r--r--compiler/linker/relative_patcher.h20
-rw-r--r--compiler/linker/relative_patcher_test.h26
-rw-r--r--compiler/linker/x86/relative_patcher_x86.h6
-rw-r--r--compiler/linker/x86/relative_patcher_x86_base.cc6
-rw-r--r--compiler/linker/x86/relative_patcher_x86_base.h6
-rw-r--r--compiler/linker/x86_64/relative_patcher_x86_64.cc3
-rw-r--r--compiler/linker/x86_64/relative_patcher_x86_64.h6
-rw-r--r--compiler/oat_test.cc33
-rw-r--r--compiler/oat_writer.cc97
-rw-r--r--compiler/oat_writer.h26
-rw-r--r--compiler/optimizing/bounds_check_elimination.cc24
-rw-r--r--compiler/optimizing/builder.cc17
-rw-r--r--compiler/optimizing/code_generator.cc19
-rw-r--r--compiler/optimizing/code_generator.h12
-rw-r--r--compiler/optimizing/code_generator_arm.cc51
-rw-r--r--compiler/optimizing/code_generator_arm.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc52
-rw-r--r--compiler/optimizing/code_generator_arm64.h5
-rw-r--r--compiler/optimizing/code_generator_mips.cc43
-rw-r--r--compiler/optimizing/code_generator_mips.h5
-rw-r--r--compiler/optimizing/code_generator_mips64.cc43
-rw-r--r--compiler/optimizing/code_generator_mips64.h5
-rw-r--r--compiler/optimizing/code_generator_x86.cc59
-rw-r--r--compiler/optimizing/code_generator_x86.h1
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc62
-rw-r--r--compiler/optimizing/code_generator_x86_64.h2
-rw-r--r--compiler/optimizing/constant_folding.h2
-rw-r--r--compiler/optimizing/graph_checker.cc5
-rw-r--r--compiler/optimizing/induction_var_analysis.cc98
-rw-r--r--compiler/optimizing/induction_var_analysis.h4
-rw-r--r--compiler/optimizing/induction_var_range.cc359
-rw-r--r--compiler/optimizing/induction_var_range.h25
-rw-r--r--compiler/optimizing/induction_var_range_test.cc101
-rw-r--r--compiler/optimizing/inliner.cc282
-rw-r--r--compiler/optimizing/inliner.h59
-rw-r--r--compiler/optimizing/intrinsics.cc1
-rw-r--r--compiler/optimizing/intrinsics.h4
-rw-r--r--compiler/optimizing/intrinsics_arm.cc89
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc3
-rw-r--r--compiler/optimizing/intrinsics_mips.cc593
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc155
-rw-r--r--compiler/optimizing/intrinsics_utils.h2
-rw-r--r--compiler/optimizing/licm.cc1
-rw-r--r--compiler/optimizing/licm.h5
-rw-r--r--compiler/optimizing/licm_test.cc2
-rw-r--r--compiler/optimizing/nodes.cc77
-rw-r--r--compiler/optimizing/nodes.h75
-rw-r--r--compiler/optimizing/optimizing_compiler.cc8
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h10
-rw-r--r--compiler/optimizing/select_generator.cc2
-rw-r--r--compiler/optimizing/select_generator.h4
-rw-r--r--compiler/profile_assistant.h72
-rw-r--r--compiler/utils/arm/assembler_arm.h3
-rw-r--r--compiler/utils/arm/assembler_arm32.cc44
-rw-r--r--compiler/utils/arm/assembler_arm32.h8
-rw-r--r--compiler/utils/arm/assembler_arm32_test.cc12
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc77
-rw-r--r--compiler/utils/arm/assembler_thumb2.h14
-rw-r--r--compiler/utils/arm/assembler_thumb2_test.cc24
-rw-r--r--compiler/utils/mips/assembler_mips.cc160
-rw-r--r--compiler/utils/mips/assembler_mips.h54
-rw-r--r--dex2oat/Android.mk30
-rw-r--r--dex2oat/dex2oat.cc212
-rw-r--r--oatdump/oatdump.cc4
-rw-r--r--profman/Android.mk45
-rw-r--r--profman/profile_assistant.cc (renamed from compiler/profile_assistant.cc)120
-rw-r--r--profman/profile_assistant.h72
-rw-r--r--profman/profile_assistant_test.cc (renamed from compiler/profile_assistant_test.cc)200
-rw-r--r--profman/profman.cc208
-rw-r--r--runtime/arch/mips/registers_mips.h1
-rw-r--r--runtime/arch/mips64/registers_mips64.h1
-rw-r--r--runtime/arch/stub_test.cc21
-rw-r--r--runtime/art_method.h8
-rw-r--r--runtime/base/arena_allocator.cc9
-rw-r--r--runtime/base/arena_allocator.h2
-rw-r--r--runtime/base/logging.h1
-rw-r--r--runtime/base/scoped_flock.cc10
-rw-r--r--runtime/base/unix_file/fd_file.cc31
-rw-r--r--runtime/base/unix_file/fd_file.h4
-rw-r--r--runtime/class_linker.cc24
-rw-r--r--runtime/class_linker_test.cc14
-rw-r--r--runtime/gc/allocation_record.cc36
-rw-r--r--runtime/gc/allocator/rosalloc.cc6
-rw-r--r--runtime/gc/allocator/rosalloc.h1
-rw-r--r--runtime/gc/collector/concurrent_copying.cc4
-rw-r--r--runtime/gc/collector/immune_region.h4
-rw-r--r--runtime/gc/collector/immune_spaces.cc18
-rw-r--r--runtime/gc/collector/immune_spaces_test.cc44
-rw-r--r--runtime/gc/heap.cc23
-rw-r--r--runtime/gc/heap.h2
-rw-r--r--runtime/gc/space/memory_tool_malloc_space-inl.h6
-rw-r--r--runtime/image.h2
-rw-r--r--runtime/instrumentation.h8
-rw-r--r--runtime/interpreter/interpreter.cc9
-rw-r--r--runtime/interpreter/interpreter_common.h8
-rw-r--r--runtime/interpreter/mterp/arm/bincmp.S28
-rw-r--r--runtime/interpreter/mterp/arm/footer.S30
-rw-r--r--runtime/interpreter/mterp/arm/header.S11
-rw-r--r--runtime/interpreter/mterp/arm/invoke.S3
-rw-r--r--runtime/interpreter/mterp/arm/op_goto.S24
-rw-r--r--runtime/interpreter/mterp/arm/op_goto_16.S20
-rw-r--r--runtime/interpreter/mterp/arm/op_goto_32.S20
-rw-r--r--runtime/interpreter/mterp/arm/op_packed_switch.S18
-rw-r--r--runtime/interpreter/mterp/arm/op_shl_long.S14
-rw-r--r--runtime/interpreter/mterp/arm/op_shl_long_2addr.S14
-rw-r--r--runtime/interpreter/mterp/arm/op_shr_long.S14
-rw-r--r--runtime/interpreter/mterp/arm/op_shr_long_2addr.S14
-rw-r--r--runtime/interpreter/mterp/arm/op_ushr_long.S14
-rw-r--r--runtime/interpreter/mterp/arm/op_ushr_long_2addr.S14
-rw-r--r--runtime/interpreter/mterp/arm/zcmp.S28
-rw-r--r--runtime/interpreter/mterp/arm64/bincmp.S29
-rw-r--r--runtime/interpreter/mterp/arm64/footer.S20
-rw-r--r--runtime/interpreter/mterp/arm64/header.S11
-rw-r--r--runtime/interpreter/mterp/arm64/invoke.S3
-rw-r--r--runtime/interpreter/mterp/arm64/op_goto.S25
-rw-r--r--runtime/interpreter/mterp/arm64/op_goto_16.S21
-rw-r--r--runtime/interpreter/mterp/arm64/op_goto_32.S23
-rw-r--r--runtime/interpreter/mterp/arm64/op_iget.S3
-rw-r--r--runtime/interpreter/mterp/arm64/op_packed_switch.S26
-rw-r--r--runtime/interpreter/mterp/arm64/zcmp.S31
-rw-r--r--runtime/interpreter/mterp/mterp.cc42
-rw-r--r--runtime/interpreter/mterp/mterp.h1
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm.S597
-rw-r--r--runtime/interpreter/mterp/out/mterp_arm64.S554
-rw-r--r--runtime/interpreter/mterp/out/mterp_x86.S542
-rw-r--r--runtime/interpreter/mterp/x86/bincmp.S17
-rw-r--r--runtime/interpreter/mterp/x86/bindiv.S4
-rw-r--r--runtime/interpreter/mterp/x86/footer.S18
-rw-r--r--runtime/interpreter/mterp/x86/header.S35
-rw-r--r--runtime/interpreter/mterp/x86/invoke.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_aget_object.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_aput_object.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_check_cast.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_const_class.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_const_string.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_const_string_jumbo.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_fill_array_data.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_filled_new_array.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_goto.S15
-rw-r--r--runtime/interpreter/mterp/x86/op_goto_16.S15
-rw-r--r--runtime/interpreter/mterp/x86/op_goto_32.S15
-rw-r--r--runtime/interpreter/mterp/x86/op_iget.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_iget_object_quick.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_iget_wide.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_instance_of.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_iput.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_iput_object.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_iput_object_quick.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_iput_wide.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_monitor_enter.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_monitor_exit.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_mul_int_2addr.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_mul_int_lit16.S8
-rw-r--r--runtime/interpreter/mterp/x86/op_mul_int_lit8.S8
-rw-r--r--runtime/interpreter/mterp/x86/op_new_array.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_new_instance.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_packed_switch.S14
-rw-r--r--runtime/interpreter/mterp/x86/op_sget.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_sget_wide.S2
-rw-r--r--runtime/interpreter/mterp/x86/op_sput.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_sput_object.S4
-rw-r--r--runtime/interpreter/mterp/x86/op_sput_wide.S4
-rw-r--r--runtime/interpreter/mterp/x86/zcmp.S17
-rw-r--r--runtime/interpreter/unstarted_runtime.cc10
-rw-r--r--runtime/interpreter/unstarted_runtime_list.h1
-rw-r--r--runtime/jit/jit.cc13
-rw-r--r--runtime/jit/jit.h7
-rw-r--r--runtime/jit/jit_code_cache.cc219
-rw-r--r--runtime/jit/jit_code_cache.h32
-rw-r--r--runtime/jit/offline_profiling_info.cc10
-rw-r--r--runtime/jit/offline_profiling_info.h7
-rw-r--r--runtime/mem_map.cc34
-rw-r--r--runtime/mem_map_test.cc13
-rw-r--r--runtime/mirror/class-inl.h5
-rw-r--r--runtime/mirror/class.cc6
-rw-r--r--runtime/mirror/class.h5
-rw-r--r--runtime/mirror/object-inl.h2
-rw-r--r--runtime/modifiers.h5
-rw-r--r--runtime/oat_file.h7
-rw-r--r--runtime/parsed_options.cc1
-rw-r--r--runtime/quick_exception_handler.cc11
-rw-r--r--runtime/runtime.cc5
-rw-r--r--runtime/runtime.h3
-rw-r--r--runtime/safe_map.h8
-rw-r--r--runtime/thread.h24
-rw-r--r--runtime/utils.cc27
-rw-r--r--runtime/utils.h3
-rw-r--r--test/004-JniTest/expected.txt27
-rw-r--r--test/004-JniTest/jni_test.cc82
-rw-r--r--test/004-JniTest/smali/AbstractInterface.smali26
-rw-r--r--test/004-JniTest/smali/ConcreteClass.smali72
-rw-r--r--test/004-JniTest/smali/ConflictInterface.smali35
-rw-r--r--test/004-JniTest/smali/DefaultInterface.smali77
-rw-r--r--test/004-JniTest/src/Main.java35
-rw-r--r--test/082-inline-execute/src/Main.java6
-rw-r--r--test/130-hprof/src-ex/Allocator.java22
-rw-r--r--test/130-hprof/src/Main.java67
-rw-r--r--test/449-checker-bce/src/Main.java36
-rw-r--r--test/530-checker-loops/src/Main.java819
-rw-r--r--test/530-checker-loops2/expected.txt0
-rw-r--r--test/530-checker-loops2/info.txt1
-rw-r--r--test/530-checker-loops2/src/Main.java999
-rw-r--r--test/550-checker-multiply-accumulate/src/Main.java28
-rw-r--r--test/551-checker-shifter-operand/src/Main.java28
-rw-r--r--test/565-checker-doublenegbitwise/src/Main.java28
-rw-r--r--test/576-polymorphic-inlining/expected.txt0
-rw-r--r--test/576-polymorphic-inlining/info.txt1
-rw-r--r--test/576-polymorphic-inlining/src/Main.java103
-rw-r--r--tools/libcore_failures.txt12
-rw-r--r--tools/libcore_failures_concurrent_collector.txt13
-rwxr-xr-xtools/setup-buildbot-device.sh3
-rwxr-xr-xtools/symbolize-buildbot-crashes.sh23
243 files changed, 7746 insertions, 3804 deletions
diff --git a/Android.mk b/Android.mk
index 2e05d33209..e762814385 100644
--- a/Android.mk
+++ b/Android.mk
@@ -86,6 +86,7 @@ include $(art_path)/disassembler/Android.mk
include $(art_path)/oatdump/Android.mk
include $(art_path)/imgdiag/Android.mk
include $(art_path)/patchoat/Android.mk
+include $(art_path)/profman/Android.mk
include $(art_path)/dalvikvm/Android.mk
include $(art_path)/tools/Android.mk
include $(art_path)/tools/ahat/Android.mk
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index dc5385309a..02bce411b4 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -118,8 +118,7 @@ endif
ART_TARGET_CLANG_arm := false
ART_TARGET_CLANG_arm64 :=
ART_TARGET_CLANG_mips :=
-# b/25928358, illegal instruction on mips64r6 with -O0
-ART_TARGET_CLANG_mips64 := false
+ART_TARGET_CLANG_mips64 :=
ART_TARGET_CLANG_x86 :=
ART_TARGET_CLANG_x86_64 :=
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index ab7036717a..c9af1c67a4 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -205,7 +205,7 @@ define build-art-test-dex
LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
- LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true
+ LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
endif
include $(BUILD_JAVA_LIBRARY)
$(5) := $$(LOCAL_INSTALLED_MODULE)
@@ -221,7 +221,7 @@ define build-art-test-dex
LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
- LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp -D jack.dex.output.multidex.legacy=true
+ LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
endif
include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
$(6) := $$(LOCAL_INSTALLED_MODULE)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 704d69a37a..b3832ac3b6 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -144,6 +144,12 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_default_no-pic_32) \
oatdump
+# Profile assistant tests requires profman utility.
+ART_GTEST_profile_assistant_test_HOST_DEPS := \
+ $(HOST_OUT_EXECUTABLES)/profmand
+ART_GTEST_profile_assistant_test_TARGET_DEPS := \
+ profman
+
# The path for which all the source files are relative, not actually the current directory.
LOCAL_PATH := art
@@ -153,6 +159,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \
dexlist/dexlist_test.cc \
imgdiag/imgdiag_test.cc \
oatdump/oatdump_test.cc \
+ profman/profile_assistant_test.cc \
runtime/arch/arch_test.cc \
runtime/arch/instruction_set_test.cc \
runtime/arch/instruction_set_features_test.cc \
@@ -251,6 +258,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \
compiler/elf_writer_test.cc \
compiler/image_test.cc \
compiler/jni/jni_compiler_test.cc \
+ compiler/linker/multi_oat_relative_patcher_test.cc \
compiler/linker/output_stream_test.cc \
compiler/oat_test.cc \
compiler/optimizing/bounds_check_elimination_test.cc \
@@ -270,7 +278,6 @@ COMPILER_GTEST_COMMON_SRC_FILES := \
compiler/optimizing/ssa_test.cc \
compiler/optimizing/stack_map_test.cc \
compiler/optimizing/suspend_check_test.cc \
- compiler/profile_assistant_test.cc \
compiler/utils/arena_allocator_test.cc \
compiler/utils/dedupe_set_test.cc \
compiler/utils/swap_space_test.cc \
@@ -571,9 +578,6 @@ define define-art-gtest
LOCAL_MODULE_PATH_64 := $$(ART_TARGET_NATIVETEST_OUT)/$$(ART_TARGET_ARCH_64)
LOCAL_MULTILIB := both
LOCAL_CLANG_CFLAGS += -Wno-used-but-marked-unused -Wno-deprecated -Wno-missing-noreturn # gtest issue
- # clang fails to compile art/runtime/arch/stub_test.cc for arm64 without -O1
- # b/26275713
- LOCAL_CLANG_CFLAGS_arm64 += -O1
include $$(BUILD_EXECUTABLE)
library_path :=
2nd_library_path :=
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index dc2c9c954a..81b854e9c3 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -291,6 +291,13 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) {
}
{
+ const char* log_args = "-verbose:collector";
+ LogVerbosity log_verbosity = LogVerbosity();
+ log_verbosity.collector = true;
+ EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+ }
+
+ {
const char* log_args = "-verbose:oat";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.oat = true;
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 740199d541..c0a00cce70 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -584,6 +584,8 @@ struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
for (size_t j = 0; j < verbose_options.size(); ++j) {
if (verbose_options[j] == "class") {
log_verbosity.class_linker = true;
+ } else if (verbose_options[j] == "collector") {
+ log_verbosity.collector = true;
} else if (verbose_options[j] == "compiler") {
log_verbosity.compiler = true;
} else if (verbose_options[j] == "deopt") {
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 159e9cfb5e..2cbafea573 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -61,6 +61,7 @@ LIBART_COMPILER_SRC_FILES := \
driver/dex_compilation_unit.cc \
linker/buffered_output_stream.cc \
linker/file_output_stream.cc \
+ linker/multi_oat_relative_patcher.cc \
linker/output_stream.cc \
linker/vector_output_stream.cc \
linker/relative_patcher.cc \
@@ -107,8 +108,7 @@ LIBART_COMPILER_SRC_FILES := \
elf_writer.cc \
elf_writer_quick.cc \
image_writer.cc \
- oat_writer.cc \
- profile_assistant.cc
+ oat_writer.cc
LIBART_COMPILER_SRC_FILES_arm := \
dex/quick/arm/assemble_arm.cc \
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index e4bfac9ee7..239bc590e9 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -194,16 +194,15 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe
kind,
isa,
instruction_set_features_.get(),
- true,
+ /* boot_image */ true,
GetImageClasses(),
GetCompiledClasses(),
GetCompiledMethods(),
- 2,
- true,
- true,
+ /* thread_count */ 2,
+ /* dump_stats */ true,
+ /* dump_passes */ true,
timer_.get(),
- -1,
- /* dex_to_oat_map */ nullptr,
+ /* swap_fd */ -1,
GetProfileCompilationInfo()));
// We typically don't generate an image in unit tests, disable this optimization by default.
compiler_driver_->SetSupportBootImageFixup(false);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index 0cd41bbf4c..6c6c9cfb1e 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -84,17 +84,16 @@ class QuickCFITest : public CFITest {
Compiler::kQuick,
isa,
isa_features.get(),
- false,
- nullptr,
- nullptr,
- nullptr,
- 0,
- false,
- false,
- 0,
- -1,
- nullptr,
- nullptr);
+ /* boot_image */ false,
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 0,
+ /* dump_stats */ false,
+ /* dump_passes */ false,
+ /* timer */ nullptr,
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr);
ClassLinker* linker = nullptr;
CompilationUnit cu(&pool, isa, &driver, linker);
DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index efdc333261..ff0ecea94c 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -64,18 +64,17 @@ class QuickAssembleX86TestBase : public testing::Test {
method_inliner_map_.get(),
Compiler::kQuick,
isa_,
- nullptr,
- false,
- nullptr,
- nullptr,
- nullptr,
- 0,
- false,
- false,
- 0,
- -1,
- nullptr,
- nullptr));
+ /* instruction_set_features*/ nullptr,
+ /* boot_image */ false,
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 0,
+ /* dump_stats */ false,
+ /* dump_passes */ false,
+ /* timer */ nullptr,
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr));
cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr));
DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>(
cu_->arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc));
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 2e2d1f99f3..0695cb56b3 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -32,19 +32,19 @@ TEST(CompiledMethodStorage, Deduplicate) {
CompilerDriver driver(&compiler_options,
&verification_results,
&method_inliner_map,
- Compiler::kOptimizing, kNone,
- nullptr,
- false,
- nullptr,
- nullptr,
- nullptr,
- 1u,
- false,
- false,
- nullptr,
- -1,
- nullptr,
- nullptr);
+ Compiler::kOptimizing,
+ /* instruction_set_ */ kNone,
+ /* instruction_set_features */ nullptr,
+ /* boot_image */ false,
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 1u,
+ /* dump_stats */ false,
+ /* dump_passes */ false,
+ /* timer */ nullptr,
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr);
CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
ASSERT_TRUE(storage->DedupeEnabled()); // The default.
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 0d65bc7405..3cb63e7082 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -186,13 +186,7 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef
} else {
// Search dex file for localized ssb index, may fail if member's class is a parent
// of the class mentioned in the dex file and there is no dex cache entry.
- std::string temp;
- const DexFile::TypeId* type_id =
- dex_file->FindTypeId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp));
- if (type_id != nullptr) {
- // medium path, needs check of static storage base being initialized
- storage_idx = dex_file->GetIndexForTypeId(*type_id);
- }
+ storage_idx = resolved_member->GetDeclaringClass()->FindTypeIndexInOtherDexFile(*dex_file);
}
if (storage_idx != DexFile::kDexNoIndex) {
*storage_index = storage_idx;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 670fe94988..a9fec30bfe 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -342,12 +342,15 @@ CompilerDriver::CompilerDriver(
Compiler::Kind compiler_kind,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
- bool boot_image, std::unordered_set<std::string>* image_classes,
+ bool boot_image,
+ std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
- size_t thread_count, bool dump_stats, bool dump_passes,
- CumulativeLogger* timer, int swap_fd,
- const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map,
+ size_t thread_count,
+ bool dump_stats,
+ bool dump_passes,
+ CumulativeLogger* timer,
+ int swap_fd,
const ProfileCompilationInfo* profile_compilation_info)
: compiler_options_(compiler_options),
verification_results_(verification_results),
@@ -374,11 +377,9 @@ CompilerDriver::CompilerDriver(
compiler_context_(nullptr),
support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
dex_files_for_oat_file_(nullptr),
- dex_file_oat_filename_map_(dex_to_oat_map),
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info) {
DCHECK(compiler_options_ != nullptr);
- DCHECK(verification_results_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
compiler_->Init();
@@ -1078,10 +1079,8 @@ static void MaybeAddToImageClasses(Handle<mirror::Class> c,
image_classes);
}
for (auto& m : c->GetVirtualMethods(pointer_size)) {
- if (m.IsMiranda() || (true)) {
- StackHandleScope<1> hs2(self);
- MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
- }
+ StackHandleScope<1> hs2(self);
+ MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
}
if (klass->IsArrayClass()) {
StackHandleScope<1> hs2(self);
@@ -1678,12 +1677,6 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
use_dex_cache = true;
}
}
- if (!use_dex_cache && IsBootImage()) {
- if (!AreInSameOatFile(&(const_cast<mirror::Class*>(referrer_class)->GetDexFile()),
- &declaring_class->GetDexFile())) {
- use_dex_cache = true;
- }
- }
// 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;
@@ -2493,6 +2486,7 @@ void CompilerDriver::Compile(jobject class_loader,
parallel_thread_pool_.get(),
parallel_thread_count_,
timings);
+ Runtime::Current()->ReclaimArenaPoolMemory();
}
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 5e35cbb309..42a5bc15e4 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -94,9 +94,11 @@ class CompilerDriver {
bool boot_image, std::unordered_set<std::string>* image_classes,
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
- size_t thread_count, bool dump_stats, bool dump_passes,
- CumulativeLogger* timer, int swap_fd,
- const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map,
+ size_t thread_count,
+ bool dump_stats,
+ bool dump_passes,
+ CumulativeLogger* timer,
+ int swap_fd,
const ProfileCompilationInfo* profile_compilation_info);
~CompilerDriver();
@@ -113,20 +115,6 @@ class CompilerDriver {
: ArrayRef<const DexFile* const>();
}
- // Are the given dex files compiled into the same oat file? Should only be called after
- // GetDexFilesForOatFile, as the conservative answer (when we don't have a map) is true.
- bool AreInSameOatFile(const DexFile* d1, const DexFile* d2) {
- if (dex_file_oat_filename_map_ == nullptr) {
- // TODO: Check for this wrt/ apps and boot image calls.
- return true;
- }
- auto it1 = dex_file_oat_filename_map_->find(d1);
- DCHECK(it1 != dex_file_oat_filename_map_->end());
- auto it2 = dex_file_oat_filename_map_->find(d2);
- DCHECK(it2 != dex_file_oat_filename_map_->end());
- return it1->second == it2->second;
- }
-
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
@@ -138,6 +126,7 @@ class CompilerDriver {
REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_);
VerificationResults* GetVerificationResults() const {
+ DCHECK(Runtime::Current()->IsAotCompiler());
return verification_results_;
}
@@ -700,9 +689,6 @@ class CompilerDriver {
// List of dex files that will be stored in the oat file.
const std::vector<const DexFile*>* dex_files_for_oat_file_;
- // Map from dex files to the oat file (name) they will be compiled into.
- const std::unordered_map<const DexFile*, const char*>* dex_file_oat_filename_map_;
-
CompiledMethodStorage compiled_method_storage_;
// Info for profile guided compilation.
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index b673eeb3b6..f7da609e5d 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -86,12 +86,24 @@ class ElfBuilder FINAL {
// Base class of all sections.
class Section : public OutputStream {
public:
- Section(ElfBuilder<ElfTypes>* owner, const std::string& name,
- Elf_Word type, Elf_Word flags, const Section* link,
- Elf_Word info, Elf_Word align, Elf_Word entsize)
- : OutputStream(name), owner_(owner), header_(),
- section_index_(0), name_(name), link_(link),
- started_(false), finished_(false), phdr_flags_(PF_R), phdr_type_(0) {
+ Section(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word type,
+ Elf_Word flags,
+ const Section* link,
+ Elf_Word info,
+ Elf_Word align,
+ Elf_Word entsize)
+ : OutputStream(name),
+ owner_(owner),
+ header_(),
+ section_index_(0),
+ name_(name),
+ link_(link),
+ started_(false),
+ finished_(false),
+ phdr_flags_(PF_R),
+ phdr_type_(0) {
DCHECK_GE(align, 1u);
header_.sh_type = type;
header_.sh_flags = flags;
@@ -228,12 +240,84 @@ class ElfBuilder FINAL {
DISALLOW_COPY_AND_ASSIGN(Section);
};
- // Writer of .dynstr .strtab and .shstrtab sections.
+ class CachedSection : public Section {
+ public:
+ CachedSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word type,
+ Elf_Word flags,
+ const Section* link,
+ Elf_Word info,
+ Elf_Word align,
+ Elf_Word entsize)
+ : Section(owner, name, type, flags, link, info, align, entsize), cache_() { }
+
+ Elf_Word Add(const void* data, size_t length) {
+ Elf_Word offset = cache_.size();
+ const uint8_t* d = reinterpret_cast<const uint8_t*>(data);
+ cache_.insert(cache_.end(), d, d + length);
+ return offset;
+ }
+
+ Elf_Word GetCacheSize() {
+ return cache_.size();
+ }
+
+ void Write() {
+ this->WriteFully(cache_.data(), cache_.size());
+ cache_.clear();
+ cache_.shrink_to_fit();
+ }
+
+ void WriteCachedSection() {
+ this->Start();
+ Write();
+ this->End();
+ }
+
+ private:
+ std::vector<uint8_t> cache_;
+ };
+
+ // Writer of .dynstr section.
+ class CachedStringSection FINAL : public CachedSection {
+ public:
+ CachedStringSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word flags,
+ Elf_Word align)
+ : CachedSection(owner,
+ name,
+ SHT_STRTAB,
+ flags,
+ /* link */ nullptr,
+ /* info */ 0,
+ align,
+ /* entsize */ 0) { }
+
+ Elf_Word Add(const std::string& name) {
+ if (CachedSection::GetCacheSize() == 0u) {
+ DCHECK(name.empty());
+ }
+ return CachedSection::Add(name.c_str(), name.length() + 1);
+ }
+ };
+
+ // Writer of .strtab and .shstrtab sections.
class StringSection FINAL : public Section {
public:
- StringSection(ElfBuilder<ElfTypes>* owner, const std::string& name,
- Elf_Word flags, Elf_Word align)
- : Section(owner, name, SHT_STRTAB, flags, nullptr, 0, align, 0),
+ StringSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word flags,
+ Elf_Word align)
+ : Section(owner,
+ name,
+ SHT_STRTAB,
+ flags,
+ /* link */ nullptr,
+ /* info */ 0,
+ align,
+ /* entsize */ 0),
current_offset_(0) {
}
@@ -252,42 +336,60 @@ class ElfBuilder FINAL {
};
// Writer of .dynsym and .symtab sections.
- class SymbolSection FINAL : public Section {
+ class SymbolSection FINAL : public CachedSection {
public:
- SymbolSection(ElfBuilder<ElfTypes>* owner, const std::string& name,
- Elf_Word type, Elf_Word flags, StringSection* strtab)
- : Section(owner, name, type, flags, strtab, 0,
- sizeof(Elf_Off), sizeof(Elf_Sym)) {
+ SymbolSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word type,
+ Elf_Word flags,
+ Section* strtab)
+ : CachedSection(owner,
+ name,
+ type,
+ flags,
+ strtab,
+ /* info */ 0,
+ sizeof(Elf_Off),
+ sizeof(Elf_Sym)) {
+ // The symbol table always has to start with NULL symbol.
+ Elf_Sym null_symbol = Elf_Sym();
+ CachedSection::Add(&null_symbol, sizeof(null_symbol));
}
// Buffer symbol for this section. It will be written later.
// If the symbol's section is null, it will be considered absolute (SHN_ABS).
// (we use this in JIT to reference code which is stored outside the debug ELF file)
- void Add(Elf_Word name, const Section* section,
- Elf_Addr addr, bool is_relative, Elf_Word size,
- uint8_t binding, uint8_t type, uint8_t other = 0) {
+ void Add(Elf_Word name,
+ const Section* section,
+ Elf_Addr addr,
+ bool is_relative,
+ Elf_Word size,
+ uint8_t binding,
+ uint8_t type,
+ uint8_t other = 0) {
+ DCHECK(section != nullptr || !is_relative);
+ Elf_Addr abs_addr = addr + (is_relative ? section->GetAddress() : 0);
+ Elf_Word section_index =
+ (section != nullptr) ? section->GetSectionIndex() : static_cast<Elf_Word>(SHN_ABS);
+ Add(name, section_index, abs_addr, size, binding, type, other);
+ }
+
+ void Add(Elf_Word name,
+ Elf_Word section_index,
+ Elf_Addr addr,
+ Elf_Word size,
+ uint8_t binding,
+ uint8_t type,
+ uint8_t other = 0) {
Elf_Sym sym = Elf_Sym();
sym.st_name = name;
- sym.st_value = addr + (is_relative ? section->GetAddress() : 0);
+ sym.st_value = addr;
sym.st_size = size;
sym.st_other = other;
- sym.st_shndx = (section != nullptr ? section->GetSectionIndex()
- : static_cast<Elf_Word>(SHN_ABS));
+ sym.st_shndx = section_index;
sym.st_info = (binding << 4) + (type & 0xf);
- symbols_.push_back(sym);
- }
-
- void Write() {
- // The symbol table always has to start with NULL symbol.
- Elf_Sym null_symbol = Elf_Sym();
- this->WriteFully(&null_symbol, sizeof(null_symbol));
- this->WriteFully(symbols_.data(), symbols_.size() * sizeof(symbols_[0]));
- symbols_.clear();
- symbols_.shrink_to_fit();
+ CachedSection::Add(&sym, sizeof(sym));
}
-
- private:
- std::vector<Elf_Sym> symbols_;
};
ElfBuilder(InstructionSet isa, OutputStream* output)
@@ -309,6 +411,8 @@ class ElfBuilder FINAL {
debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
shstrtab_(this, ".shstrtab", 0, 1),
started_(false),
+ write_program_headers_(false),
+ loaded_size_(0u),
virtual_address_(0) {
text_.phdr_flags_ = PF_R | PF_X;
bss_.phdr_flags_ = PF_R | PF_W;
@@ -380,6 +484,14 @@ class ElfBuilder FINAL {
void End() {
DCHECK(started_);
+ // Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss,
+ // .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_.
+ // TODO: Either refactor the .eh_frame creation so that it counts towards loaded_size_,
+ // or remove all support for .eh_frame. (The currently unused .eh_frame counts towards
+ // the virtual_address_ but we don't consider it for loaded_size_.)
+ CHECK(loaded_size_ == 0 || loaded_size_ == RoundUp(virtual_address_, kPageSize))
+ << loaded_size_ << " " << virtual_address_;
+
// Write section names and finish the section headers.
shstrtab_.Start();
shstrtab_.Write("");
@@ -434,45 +546,58 @@ class ElfBuilder FINAL {
// information like the address and size of .rodata and .text.
// It also contains other metadata like the SONAME.
// The .dynamic section is found using the PT_DYNAMIC program header.
- void WriteDynamicSection(const std::string& elf_file_path) {
+ void PrepareDynamicSection(const std::string& elf_file_path,
+ Elf_Word rodata_size,
+ Elf_Word text_size,
+ Elf_Word bss_size) {
std::string soname(elf_file_path);
size_t directory_separator_pos = soname.rfind('/');
if (directory_separator_pos != std::string::npos) {
soname = soname.substr(directory_separator_pos + 1);
}
- dynstr_.Start();
- dynstr_.Write(""); // dynstr should start with empty string.
- dynsym_.Add(dynstr_.Write("oatdata"), &rodata_, 0, true,
- rodata_.GetSize(), STB_GLOBAL, STT_OBJECT);
- if (text_.GetSize() != 0u) {
- dynsym_.Add(dynstr_.Write("oatexec"), &text_, 0, true,
- text_.GetSize(), STB_GLOBAL, STT_OBJECT);
- dynsym_.Add(dynstr_.Write("oatlastword"), &text_, text_.GetSize() - 4,
- true, 4, STB_GLOBAL, STT_OBJECT);
- } else if (rodata_.GetSize() != 0) {
+ // Calculate addresses of .text, .bss and .dynstr.
+ DCHECK_EQ(rodata_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ DCHECK_EQ(text_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ DCHECK_EQ(bss_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ Elf_Word rodata_address = rodata_.GetAddress();
+ Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize);
+ Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize);
+ Elf_Word dynstr_address = RoundUp(bss_address + bss_size, kPageSize);
+
+ // Cache .dynstr, .dynsym and .hash data.
+ dynstr_.Add(""); // dynstr should start with empty string.
+ Elf_Word rodata_index = rodata_.GetSectionIndex();
+ Elf_Word oatdata = dynstr_.Add("oatdata");
+ dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT);
+ if (text_size != 0u) {
+ Elf_Word text_index = rodata_index + 1u;
+ Elf_Word oatexec = dynstr_.Add("oatexec");
+ dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT);
+ Elf_Word oatlastword = dynstr_.Add("oatlastword");
+ Elf_Word oatlastword_address = text_address + text_size - 4;
+ dynsym_.Add(oatlastword, text_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
+ } else if (rodata_size != 0) {
// rodata_ can be size 0 for dwarf_test.
- dynsym_.Add(dynstr_.Write("oatlastword"), &rodata_, rodata_.GetSize() - 4,
- true, 4, STB_GLOBAL, STT_OBJECT);
+ Elf_Word oatlastword = dynstr_.Add("oatlastword");
+ Elf_Word oatlastword_address = rodata_address + rodata_size - 4;
+ dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
}
- if (bss_.finished_) {
- dynsym_.Add(dynstr_.Write("oatbss"), &bss_,
- 0, true, bss_.GetSize(), STB_GLOBAL, STT_OBJECT);
- dynsym_.Add(dynstr_.Write("oatbsslastword"), &bss_,
- bss_.GetSize() - 4, true, 4, STB_GLOBAL, STT_OBJECT);
+ if (bss_size != 0u) {
+ Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u);
+ Elf_Word oatbss = dynstr_.Add("oatbss");
+ dynsym_.Add(oatbss, bss_index, bss_address, bss_size, STB_GLOBAL, STT_OBJECT);
+ Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword");
+ Elf_Word bsslastword_address = bss_address + bss_size - 4;
+ dynsym_.Add(oatbsslastword, bss_index, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT);
}
- Elf_Word soname_offset = dynstr_.Write(soname);
- dynstr_.End();
-
- dynsym_.Start();
- dynsym_.Write();
- dynsym_.End();
+ Elf_Word soname_offset = dynstr_.Add(soname);
// We do not really need a hash-table since there is so few entries.
// However, the hash-table is the only way the linker can actually
// determine the number of symbols in .dynsym so it is required.
- hash_.Start();
- int count = dynsym_.GetSize() / sizeof(Elf_Sym); // Includes NULL.
+ int count = dynsym_.GetCacheSize() / sizeof(Elf_Sym); // Includes NULL.
std::vector<Elf_Word> hash;
hash.push_back(1); // Number of buckets.
hash.push_back(count); // Number of chains.
@@ -484,21 +609,44 @@ class ElfBuilder FINAL {
hash.push_back(i + 1); // Each symbol points to the next one.
}
hash.push_back(0); // Last symbol terminates the chain.
- hash_.WriteFully(hash.data(), hash.size() * sizeof(hash[0]));
- hash_.End();
+ hash_.Add(hash.data(), hash.size() * sizeof(hash[0]));
+
+ // Calculate addresses of .dynsym, .hash and .dynamic.
+ DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags);
+ DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags);
+ Elf_Word dynsym_address =
+ RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign);
+ Elf_Word hash_address =
+ RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign);
+ DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
+ Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize);
- dynamic_.Start();
Elf_Dyn dyns[] = {
- { DT_HASH, { hash_.GetAddress() } },
- { DT_STRTAB, { dynstr_.GetAddress() } },
- { DT_SYMTAB, { dynsym_.GetAddress() } },
+ { DT_HASH, { hash_address } },
+ { DT_STRTAB, { dynstr_address } },
+ { DT_SYMTAB, { dynsym_address } },
{ DT_SYMENT, { sizeof(Elf_Sym) } },
- { DT_STRSZ, { dynstr_.GetSize() } },
+ { DT_STRSZ, { dynstr_.GetCacheSize() } },
{ DT_SONAME, { soname_offset } },
{ DT_NULL, { 0 } },
};
- dynamic_.WriteFully(&dyns, sizeof(dyns));
- dynamic_.End();
+ dynamic_.Add(&dyns, sizeof(dyns));
+
+ loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize);
+ }
+
+ void WriteDynamicSection() {
+ dynstr_.WriteCachedSection();
+ dynsym_.WriteCachedSection();
+ hash_.WriteCachedSection();
+ dynamic_.WriteCachedSection();
+
+ CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize));
+ }
+
+ Elf_Word GetLoadedSize() {
+ CHECK_NE(loaded_size_, 0u);
+ return loaded_size_;
}
// Returns true if all writes and seeks on the output stream succeeded.
@@ -676,10 +824,10 @@ class ElfBuilder FINAL {
Section rodata_;
Section text_;
Section bss_;
- StringSection dynstr_;
+ CachedStringSection dynstr_;
SymbolSection dynsym_;
- Section hash_;
- Section dynamic_;
+ CachedSection hash_;
+ CachedSection dynamic_;
Section eh_frame_;
Section eh_frame_hdr_;
StringSection strtab_;
@@ -694,12 +842,14 @@ class ElfBuilder FINAL {
std::vector<Section*> sections_;
bool started_;
+ bool write_program_headers_;
+
+ // The size of the memory taken by the ELF file when loaded.
+ size_t loaded_size_;
// Used for allocation of virtual address space.
Elf_Addr virtual_address_;
- size_t write_program_headers_;
-
DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
};
diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h
index d50a08cb20..c9ea0083d5 100644
--- a/compiler/elf_writer.h
+++ b/compiler/elf_writer.h
@@ -52,14 +52,12 @@ class ElfWriter {
virtual ~ElfWriter() {}
virtual void Start() = 0;
- virtual void PrepareDebugInfo(size_t rodata_section_size,
- size_t text_section_size,
- const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
+ virtual void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) = 0;
+ virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
virtual OutputStream* StartRoData() = 0;
virtual void EndRoData(OutputStream* rodata) = 0;
virtual OutputStream* StartText() = 0;
virtual void EndText(OutputStream* text) = 0;
- virtual void SetBssSize(size_t bss_size) = 0;
virtual void WriteDynamicSection() = 0;
virtual void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
virtual void WritePatchLocations(const ArrayRef<const uintptr_t>& patch_locations) = 0;
@@ -70,6 +68,9 @@ class ElfWriter {
// should Seek() back to the position where the stream was before this operation.
virtual OutputStream* GetStream() = 0;
+ // Get the size that the loaded ELF file will occupy in memory.
+ virtual size_t GetLoadedSize() = 0;
+
protected:
ElfWriter() = default;
};
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 1d71e572d7..19346ecc2b 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -88,14 +88,12 @@ class ElfWriterQuick FINAL : public ElfWriter {
~ElfWriterQuick();
void Start() OVERRIDE;
- void PrepareDebugInfo(size_t rodata_section_size,
- size_t text_section_size,
- const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
+ void SetLoadedSectionSizes(size_t rodata_size, size_t text_size, size_t bss_size) OVERRIDE;
+ void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
OutputStream* StartRoData() OVERRIDE;
void EndRoData(OutputStream* rodata) OVERRIDE;
OutputStream* StartText() OVERRIDE;
void EndText(OutputStream* text) OVERRIDE;
- void SetBssSize(size_t bss_size) OVERRIDE;
void WriteDynamicSection() OVERRIDE;
void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
void WritePatchLocations(const ArrayRef<const uintptr_t>& patch_locations) OVERRIDE;
@@ -103,12 +101,17 @@ class ElfWriterQuick FINAL : public ElfWriter {
virtual OutputStream* GetStream() OVERRIDE;
+ size_t GetLoadedSize() OVERRIDE;
+
static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
std::vector<uint8_t>* buffer);
private:
const CompilerOptions* const compiler_options_;
File* const elf_file_;
+ size_t rodata_size_;
+ size_t text_size_;
+ size_t bss_size_;
std::unique_ptr<BufferedOutputStream> output_stream_;
std::unique_ptr<ElfBuilder<ElfTypes>> builder_;
std::unique_ptr<DebugInfoTask> debug_info_task_;
@@ -134,6 +137,9 @@ ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set,
: ElfWriter(),
compiler_options_(compiler_options),
elf_file_(elf_file),
+ rodata_size_(0u),
+ text_size_(0u),
+ bss_size_(0u),
output_stream_(MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file))),
builder_(new ElfBuilder<ElfTypes>(instruction_set, output_stream_.get())) {}
@@ -146,6 +152,19 @@ void ElfWriterQuick<ElfTypes>::Start() {
}
template <typename ElfTypes>
+void ElfWriterQuick<ElfTypes>::SetLoadedSectionSizes(size_t rodata_size,
+ size_t text_size,
+ size_t bss_size) {
+ DCHECK_EQ(rodata_size_, 0u);
+ rodata_size_ = rodata_size;
+ DCHECK_EQ(text_size_, 0u);
+ text_size_ = text_size;
+ DCHECK_EQ(bss_size_, 0u);
+ bss_size_ = bss_size;
+ builder_->PrepareDynamicSection(elf_file_->GetPath(), rodata_size_, text_size_, bss_size_);
+}
+
+template <typename ElfTypes>
OutputStream* ElfWriterQuick<ElfTypes>::StartRoData() {
auto* rodata = builder_->GetRoData();
rodata->Start();
@@ -172,31 +191,21 @@ void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) {
}
template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::SetBssSize(size_t bss_size) {
- auto* bss = builder_->GetBss();
- if (bss_size != 0u) {
- bss->WriteNoBitsSection(bss_size);
- }
-}
-
-template <typename ElfTypes>
void ElfWriterQuick<ElfTypes>::WriteDynamicSection() {
- builder_->WriteDynamicSection(elf_file_->GetPath());
+ if (bss_size_ != 0u) {
+ builder_->GetBss()->WriteNoBitsSection(bss_size_);
+ }
+ builder_->WriteDynamicSection();
}
template <typename ElfTypes>
void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(
- size_t rodata_section_size,
- size_t text_section_size,
const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) {
// Prepare the mini-debug-info in background while we do other I/O.
Thread* self = Thread::Current();
debug_info_task_ = std::unique_ptr<DebugInfoTask>(
- new DebugInfoTask(builder_->GetIsa(),
- rodata_section_size,
- text_section_size,
- method_infos));
+ new DebugInfoTask(builder_->GetIsa(), rodata_size_, text_size_, method_infos));
debug_info_thread_pool_ = std::unique_ptr<ThreadPool>(
new ThreadPool("Mini-debug-info writer", 1));
debug_info_thread_pool_->AddTask(self, debug_info_task_.get());
@@ -245,6 +254,11 @@ OutputStream* ElfWriterQuick<ElfTypes>::GetStream() {
return builder_->GetStream();
}
+template <typename ElfTypes>
+size_t ElfWriterQuick<ElfTypes>::GetLoadedSize() {
+ return builder_->GetLoadedSize();
+}
+
// Explicit instantiations
template class ElfWriterQuick<ElfTypes32>;
template class ElfWriterQuick<ElfTypes64>;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 4920f9baa5..992af29545 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -28,6 +28,7 @@
#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
#include "image_writer.h"
+#include "linker/multi_oat_relative_patcher.h"
#include "lock_word.h"
#include "mirror/object-inl.h"
#include "oat_writer.h"
@@ -72,10 +73,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str()));
const uintptr_t requested_image_base = ART_BASE_ADDRESS;
- std::unordered_map<const DexFile*, const char*> dex_file_to_oat_filename_map;
+ std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map;
std::vector<const char*> oat_filename_vector(1, oat_filename.c_str());
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
- dex_file_to_oat_filename_map.emplace(dex_file, oat_filename.c_str());
+ dex_file_to_oat_index_map.emplace(dex_file, 0);
}
std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_,
requested_image_base,
@@ -83,7 +84,7 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
/*compile_app_image*/false,
storage_mode,
oat_filename_vector,
- dex_file_to_oat_filename_map));
+ dex_file_to_oat_index_map));
// TODO: compile_pic should be a test argument.
{
{
@@ -123,10 +124,22 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
&opened_dex_files_map,
&opened_dex_files);
ASSERT_TRUE(dex_files_ok);
- oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files);
+
bool image_space_ok = writer->PrepareImageAddressSpace();
ASSERT_TRUE(image_space_ok);
+ linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(),
+ instruction_set_features_.get());
+ oat_writer.PrepareLayout(compiler_driver_.get(), writer.get(), dex_files, &patcher);
+ size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
+ size_t text_size = oat_writer.GetSize() - rodata_size;
+ elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize());
+
+ writer->UpdateOatFileLayout(/* oat_index */ 0u,
+ elf_writer->GetLoadedSize(),
+ oat_writer.GetOatDataOffset(),
+ oat_writer.GetSize());
+
bool rodata_ok = oat_writer.WriteRodata(rodata);
ASSERT_TRUE(rodata_ok);
elf_writer->EndRoData(rodata);
@@ -139,13 +152,13 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
bool header_ok = oat_writer.WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u);
ASSERT_TRUE(header_ok);
- elf_writer->SetBssSize(oat_writer.GetBssSize());
+ writer->UpdateOatFileHeader(/* oat_index */ 0u, oat_writer.GetOatHeader());
+
elf_writer->WriteDynamicSection();
elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
bool success = elf_writer->End();
-
ASSERT_TRUE(success);
}
}
@@ -158,12 +171,10 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
std::vector<const char*> dup_image_filename(1, image_file.GetFilename().c_str());
bool success_image = writer->Write(kInvalidFd,
dup_image_filename,
- kInvalidFd,
- dup_oat_filename,
- dup_oat_filename[0]);
+ dup_oat_filename);
ASSERT_TRUE(success_image);
bool success_fixup = ElfWriter::Fixup(dup_oat.get(),
- writer->GetOatDataBegin(dup_oat_filename[0]));
+ writer->GetOatDataBegin(0));
ASSERT_TRUE(success_fixup);
ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file "
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 73574ba673..5eff8f37ec 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -124,7 +124,10 @@ bool ImageWriter::PrepareImageAddressSpace() {
{
ScopedObjectAccess soa(Thread::Current());
PruneNonImageClasses(); // Remove junk
- ComputeLazyFieldsForImageClasses(); // Add useful information
+ if (!compile_app_image_) {
+ // Avoid for app image since this may increase RAM and image size.
+ ComputeLazyFieldsForImageClasses(); // Add useful information
+ }
}
heap->CollectGarbage(false); // Remove garbage.
@@ -159,9 +162,7 @@ bool ImageWriter::PrepareImageAddressSpace() {
bool ImageWriter::Write(int image_fd,
const std::vector<const char*>& image_filenames,
- int oat_fd,
- const std::vector<const char*>& oat_filenames,
- const std::string& oat_location) {
+ const std::vector<const char*>& oat_filenames) {
// If image_fd or oat_fd are not kInvalidFd then we may have empty strings in image_filenames or
// oat_filenames.
CHECK(!image_filenames.empty());
@@ -169,95 +170,13 @@ bool ImageWriter::Write(int image_fd,
CHECK_EQ(image_filenames.size(), 1u);
}
CHECK(!oat_filenames.empty());
- if (oat_fd != kInvalidFd) {
- CHECK_EQ(oat_filenames.size(), 1u);
- }
CHECK_EQ(image_filenames.size(), oat_filenames.size());
- size_t oat_file_offset = 0;
-
- for (size_t i = 0; i < oat_filenames.size(); ++i) {
- const char* oat_filename = oat_filenames[i];
- std::unique_ptr<File> oat_file;
-
- if (oat_fd != -1) {
- if (strlen(oat_filename) == 0u) {
- oat_file.reset(new File(oat_fd, false));
- } else {
- oat_file.reset(new File(oat_fd, oat_filename, false));
- }
- int length = oat_file->GetLength();
- if (length < 0) {
- PLOG(ERROR) << "Oat file has negative length " << length;
- return false;
- } else {
- // Leave the fd open since dex2oat still needs to write out the oat file with the fd.
- oat_file->DisableAutoClose();
- }
- } else {
- oat_file.reset(OS::OpenFileReadWrite(oat_filename));
- }
- if (oat_file == nullptr) {
- PLOG(ERROR) << "Failed to open oat file " << oat_filename;
- return false;
- }
- std::string error_msg;
- oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_filename, nullptr, &error_msg);
- if (oat_file_ == nullptr) {
- PLOG(ERROR) << "Failed to open writable oat file " << oat_filename;
- oat_file->Erase();
- return false;
- }
- Runtime::Current()->GetOatFileManager().RegisterOatFile(
- std::unique_ptr<const OatFile>(oat_file_));
-
- const OatHeader& oat_header = oat_file_->GetOatHeader();
- ImageInfo& image_info = GetImageInfo(oat_filename);
-
- size_t oat_loaded_size = 0;
- size_t oat_data_offset = 0;
- ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset);
-
- DCHECK_EQ(image_info.oat_offset_, oat_file_offset);
- oat_file_offset += oat_loaded_size;
-
- if (i == 0) {
- // Primary oat file, read the trampolines.
- image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] =
- oat_header.GetInterpreterToInterpreterBridgeOffset();
- image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] =
- oat_header.GetInterpreterToCompiledCodeBridgeOffset();
- image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] =
- oat_header.GetJniDlsymLookupOffset();
- image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] =
- oat_header.GetQuickGenericJniTrampolineOffset();
- image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] =
- oat_header.GetQuickImtConflictTrampolineOffset();
- image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] =
- oat_header.GetQuickResolutionTrampolineOffset();
- image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] =
- oat_header.GetQuickToInterpreterBridgeOffset();
- }
-
-
- {
- ScopedObjectAccess soa(Thread::Current());
- CreateHeader(oat_loaded_size, oat_data_offset);
- CopyAndFixupNativeData();
- }
-
- SetOatChecksumFromElfFile(oat_file.get());
-
- if (oat_fd != -1) {
- // Leave fd open for caller.
- if (oat_file->Flush() != 0) {
- LOG(ERROR) << "Failed to flush oat file " << oat_filename << " for " << oat_location;
- return false;
- }
- } else if (oat_file->FlushCloseOrErase() != 0) {
- LOG(ERROR) << "Failed to flush and close oat file " << oat_filename
- << " for " << oat_location;
- return false;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ for (size_t i = 0; i < oat_filenames.size(); ++i) {
+ CreateHeader(i);
+ CopyAndFixupNativeData(i);
}
}
@@ -270,8 +189,7 @@ bool ImageWriter::Write(int image_fd,
for (size_t i = 0; i < image_filenames.size(); ++i) {
const char* image_filename = image_filenames[i];
- const char* oat_filename = oat_filenames[i];
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ ImageInfo& image_info = GetImageInfo(i);
std::unique_ptr<File> image_file;
if (image_fd != kInvalidFd) {
if (strlen(image_filename) == 0u) {
@@ -393,8 +311,8 @@ void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot
DCHECK(object != nullptr);
DCHECK_NE(image_objects_offset_begin_, 0u);
- const char* oat_filename = GetOatFilename(object);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ ImageInfo& image_info = GetImageInfo(oat_index);
size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()];
size_t new_offset = bin_slot_offset + bin_slot.GetIndex();
DCHECK_ALIGNED(new_offset, kObjectAlignment);
@@ -414,8 +332,8 @@ size_t ImageWriter::GetImageOffset(mirror::Object* object) const {
DCHECK(IsImageOffsetAssigned(object));
LockWord lock_word = object->GetLockWord(false);
size_t offset = lock_word.ForwardingAddress();
- const char* oat_filename = GetOatFilename(object);
- const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
DCHECK_LT(offset, image_info.image_end_);
return offset;
}
@@ -458,8 +376,8 @@ void ImageWriter::PrepareDexCacheArraySlots() {
// Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned()
// when AssignImageBinSlot() assigns their indexes out or order.
for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) {
- auto it = dex_file_oat_filename_map_.find(dex_file);
- DCHECK(it != dex_file_oat_filename_map_.end()) << dex_file->GetLocation();
+ auto it = dex_file_oat_index_map_.find(dex_file);
+ DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
ImageInfo& image_info = GetImageInfo(it->second);
image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]);
DexCacheArraysLayout layout(target_ptr_size_, dex_file);
@@ -478,8 +396,8 @@ void ImageWriter::PrepareDexCacheArraySlots() {
const DexFile* dex_file = dex_cache->GetDexFile();
DexCacheArraysLayout layout(target_ptr_size_, dex_file);
DCHECK(layout.Valid());
- const char* oat_filename = GetOatFilenameForDexCache(dex_cache);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_index);
uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file);
DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr);
AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(),
@@ -501,9 +419,9 @@ void ImageWriter::PrepareDexCacheArraySlots() {
void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset, DexCache* dex_cache) {
if (array != nullptr) {
DCHECK(!IsInBootImage(array));
- const char* oat_filename = GetOatFilenameForDexCache(dex_cache);
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
native_object_relocations_.emplace(array,
- NativeObjectRelocation { oat_filename, offset, kNativeObjectRelocationTypeDexCacheArray });
+ NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray });
}
}
@@ -618,8 +536,8 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object) {
} // else bin = kBinRegular
}
- const char* oat_filename = GetOatFilename(object);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ ImageInfo& image_info = GetImageInfo(oat_index);
size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment
current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned).
@@ -655,8 +573,8 @@ bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const {
LockWord lock_word = object->GetLockWord(false);
size_t offset = lock_word.ForwardingAddress();
BinSlot bin_slot(offset);
- const char* oat_filename = GetOatFilename(object);
- const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()])
<< "bin slot offset should not exceed the size of that bin";
}
@@ -672,16 +590,15 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const
DCHECK_LE(offset, std::numeric_limits<uint32_t>::max());
BinSlot bin_slot(static_cast<uint32_t>(offset));
- const char* oat_filename = GetOatFilename(object);
- const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]);
return bin_slot;
}
bool ImageWriter::AllocMemory() {
- for (const char* oat_filename : oat_filenames_) {
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ for (ImageInfo& image_info : image_infos_) {
ImageSection unused_sections[ImageHeader::kSectionCount];
const size_t length = RoundUp(
image_info.CreateImageSections(target_ptr_size_, unused_sections),
@@ -735,20 +652,20 @@ bool ImageWriter::IsBootClassLoaderNonImageClass(mirror::Class* klass) {
return IsBootClassLoaderClass(klass) && !IsInBootImage(klass);
}
-bool ImageWriter::ContainsBootClassLoaderNonImageClass(mirror::Class* klass) {
+bool ImageWriter::PruneAppImageClass(mirror::Class* klass) {
bool early_exit = false;
std::unordered_set<mirror::Class*> visited;
- return ContainsBootClassLoaderNonImageClassInternal(klass, &early_exit, &visited);
+ return PruneAppImageClassInternal(klass, &early_exit, &visited);
}
-bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
+bool ImageWriter::PruneAppImageClassInternal(
mirror::Class* klass,
bool* early_exit,
std::unordered_set<mirror::Class*>* visited) {
DCHECK(early_exit != nullptr);
DCHECK(visited != nullptr);
DCHECK(compile_app_image_);
- if (klass == nullptr) {
+ if (klass == nullptr || IsInBootImage(klass)) {
return false;
}
auto found = prune_class_memo_.find(klass);
@@ -762,7 +679,11 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
return false;
}
visited->emplace(klass);
- bool result = IsBootClassLoaderNonImageClass(klass);
+ bool result = IsBootClassLoaderClass(klass);
+ std::string temp;
+ // Prune if not an image class, this handles any broken sets of image classes such as having a
+ // class in the set but not it's superclass.
+ result = result || !compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
bool my_early_exit = false; // Only for ourselves, ignore caller.
// Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the
// app image.
@@ -775,17 +696,15 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
// Check interfaces since these wont be visited through VisitReferences.)
mirror::IfTable* if_table = klass->GetIfTable();
for (size_t i = 0, num_interfaces = klass->GetIfTableCount(); i < num_interfaces; ++i) {
- result = result || ContainsBootClassLoaderNonImageClassInternal(
- if_table->GetInterface(i),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(if_table->GetInterface(i),
+ &my_early_exit,
+ visited);
}
}
if (klass->IsObjectArrayClass()) {
- result = result || ContainsBootClassLoaderNonImageClassInternal(
- klass->GetComponentType(),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(klass->GetComponentType(),
+ &my_early_exit,
+ visited);
}
// Check static fields and their classes.
size_t num_static_fields = klass->NumReferenceStaticFields();
@@ -798,27 +717,22 @@ bool ImageWriter::ContainsBootClassLoaderNonImageClassInternal(
mirror::Object* ref = klass->GetFieldObject<mirror::Object>(field_offset);
if (ref != nullptr) {
if (ref->IsClass()) {
- result = result ||
- ContainsBootClassLoaderNonImageClassInternal(
- ref->AsClass(),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(ref->AsClass(),
+ &my_early_exit,
+ visited);
+ } else {
+ result = result || PruneAppImageClassInternal(ref->GetClass(),
+ &my_early_exit,
+ visited);
}
- result = result ||
- ContainsBootClassLoaderNonImageClassInternal(
- ref->GetClass(),
- &my_early_exit,
- visited);
}
field_offset = MemberOffset(field_offset.Uint32Value() +
sizeof(mirror::HeapReference<mirror::Object>));
}
}
- result = result ||
- ContainsBootClassLoaderNonImageClassInternal(
- klass->GetSuperClass(),
- &my_early_exit,
- visited);
+ result = result || PruneAppImageClassInternal(klass->GetSuperClass(),
+ &my_early_exit,
+ visited);
// Erase the element we stored earlier since we are exiting the function.
auto it = visited->find(klass);
DCHECK(it != visited->end());
@@ -837,15 +751,21 @@ bool ImageWriter::KeepClass(Class* klass) {
if (klass == nullptr) {
return false;
}
+ if (compile_app_image_ && Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
+ // Already in boot image, return true.
+ return true;
+ }
+ std::string temp;
+ if (!compiler_driver_.IsImageClass(klass->GetDescriptor(&temp))) {
+ return false;
+ }
if (compile_app_image_) {
// For app images, we need to prune boot loader classes that are not in the boot image since
// these may have already been loaded when the app image is loaded.
// Keep classes in the boot image space since we don't want to re-resolve these.
- return Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass) ||
- !ContainsBootClassLoaderNonImageClass(klass);
+ return !PruneAppImageClass(klass);
}
- std::string temp;
- return compiler_driver_.IsImageClass(klass->GetDescriptor(&temp));
+ return true;
}
class NonImageClassesVisitor : public ClassVisitor {
@@ -873,6 +793,7 @@ void ImageWriter::PruneNonImageClasses() {
class_linker->VisitClasses(&visitor);
// Remove the undesired classes from the class roots.
+ VLOG(compiler) << "Pruning " << visitor.classes_to_prune_.size() << " classes";
for (mirror::Class* klass : visitor.classes_to_prune_) {
std::string temp;
const char* name = klass->GetDescriptor(&temp);
@@ -891,10 +812,10 @@ void ImageWriter::PruneNonImageClasses() {
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_); // For ClassInClassTable
ReaderMutexLock mu2(self, *class_linker->DexLock());
for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
- mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
- if (dex_cache == nullptr) {
+ if (self->IsJWeakCleared(data.weak_root)) {
continue;
}
+ mirror::DexCache* dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache();
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
Class* klass = dex_cache->GetResolvedType(i);
if (klass != nullptr && !KeepClass(klass)) {
@@ -907,10 +828,10 @@ void ImageWriter::PruneNonImageClasses() {
mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_);
DCHECK(method != nullptr) << "Expected resolution method instead of null method";
mirror::Class* declaring_class = method->GetDeclaringClass();
- // Miranda methods may be held live by a class which was not an image class but have a
+ // Copied methods may be held live by a class which was not an image class but have a
// declaring class which is an image class. Set it to the resolution method to be safe and
// prevent dangling pointers.
- if (method->IsMiranda() || !KeepClass(declaring_class)) {
+ if (method->IsCopied() || !KeepClass(declaring_class)) {
mirror::DexCache::SetElementPtrSize(resolved_methods,
i,
resolution_method,
@@ -970,8 +891,7 @@ void ImageWriter::DumpImageClasses() {
mirror::String* ImageWriter::FindInternedString(mirror::String* string) {
Thread* const self = Thread::Current();
- for (auto& pair : image_info_map_) {
- const ImageInfo& image_info = pair.second;
+ for (const ImageInfo& image_info : image_infos_) {
mirror::String* const found = image_info.intern_table_->LookupStrong(self, string);
DCHECK(image_info.intern_table_->LookupWeak(self, string) == nullptr)
<< string->ToModifiedUtf8();
@@ -998,8 +918,8 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) {
DCHECK(obj != nullptr);
// if it is a string, we want to intern it if its not interned.
if (obj->GetClass()->IsStringClass()) {
- const char* oat_filename = GetOatFilename(obj);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(obj);
+ ImageInfo& image_info = GetImageInfo(oat_index);
// we must be an interned string that was forward referenced and already assigned
if (IsImageBinSlotAssigned(obj)) {
@@ -1028,7 +948,7 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) {
AssignImageBinSlot(obj);
}
-ObjectArray<Object>* ImageWriter::CreateImageRoots(const char* oat_filename) const {
+ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const {
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
Thread* self = Thread::Current();
@@ -1037,10 +957,10 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(const char* oat_filename) con
class_linker->FindSystemClass(self, "[Ljava/lang/Object;")));
std::unordered_set<const DexFile*> image_dex_files;
- for (auto& pair : dex_file_oat_filename_map_) {
+ for (auto& pair : dex_file_oat_index_map_) {
const DexFile* image_dex_file = pair.first;
- const char* image_oat_filename = pair.second;
- if (strcmp(oat_filename, image_oat_filename) == 0) {
+ size_t image_oat_index = pair.second;
+ if (oat_index == image_oat_index) {
image_dex_files.insert(image_dex_file);
}
}
@@ -1165,8 +1085,8 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
LengthPrefixedArray<ArtField>* fields[] = {
as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(),
};
- const char* oat_file = GetOatFilenameForDexCache(dex_cache);
- ImageInfo& image_info = GetImageInfo(oat_file);
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_index);
{
// Note: This table is only accessed from the image writer, so the lock is technically
// unnecessary.
@@ -1184,8 +1104,11 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
<< " already forwarded";
size_t& offset = image_info.bin_slot_sizes_[kBinArtField];
DCHECK(!IsInBootImage(cur_fields));
- native_object_relocations_.emplace(cur_fields,
- NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtFieldArray });
+ native_object_relocations_.emplace(
+ cur_fields,
+ NativeObjectRelocation {
+ oat_index, offset, kNativeObjectRelocationTypeArtFieldArray
+ });
offset += header_size;
// Forward individual fields so that we can quickly find where they belong.
for (size_t i = 0, count = cur_fields->size(); i < count; ++i) {
@@ -1195,8 +1118,9 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i
<< " already assigned " << PrettyField(field) << " static=" << field->IsStatic();
DCHECK(!IsInBootImage(field));
- native_object_relocations_.emplace(field,
- NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtField });
+ native_object_relocations_.emplace(
+ field,
+ NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField });
offset += sizeof(ArtField);
}
}
@@ -1229,13 +1153,13 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
DCHECK(!IsInBootImage(array));
native_object_relocations_.emplace(array,
NativeObjectRelocation {
- oat_file,
+ oat_index,
offset,
any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty
: kNativeObjectRelocationTypeArtMethodArrayClean });
offset += header_size;
for (auto& m : as_klass->GetMethods(target_ptr_size_)) {
- AssignMethodOffset(&m, type, oat_file);
+ AssignMethodOffset(&m, type, oat_index);
}
(any_dirty ? dirty_methods_ : clean_methods_) += num_methods;
}
@@ -1263,14 +1187,14 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) {
void ImageWriter::AssignMethodOffset(ArtMethod* method,
NativeObjectRelocationType type,
- const char* oat_filename) {
+ size_t oat_index) {
DCHECK(!IsInBootImage(method));
auto it = native_object_relocations_.find(method);
CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned "
<< PrettyMethod(method);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ ImageInfo& image_info = GetImageInfo(oat_index);
size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)];
- native_object_relocations_.emplace(method, NativeObjectRelocation { oat_filename, offset, type });
+ native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type });
offset += ArtMethod::Size(target_ptr_size_);
}
@@ -1305,9 +1229,8 @@ void ImageWriter::CalculateNewObjectOffsets() {
Thread* const self = Thread::Current();
StackHandleScopeCollection handles(self);
std::vector<Handle<ObjectArray<Object>>> image_roots;
- for (const char* oat_filename : oat_filenames_) {
- std::string image_filename = oat_filename;
- image_roots.push_back(handles.NewHandle(CreateImageRoots(image_filename.c_str())));
+ for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
+ image_roots.push_back(handles.NewHandle(CreateImageRoots(i)));
}
auto* runtime = Runtime::Current();
@@ -1333,12 +1256,12 @@ void ImageWriter::CalculateNewObjectOffsets() {
const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean;
auto it = native_object_relocations_.find(&image_method_array_);
CHECK(it == native_object_relocations_.end());
- ImageInfo& default_image_info = GetImageInfo(default_oat_filename_);
+ ImageInfo& default_image_info = GetImageInfo(GetDefaultOatIndex());
size_t& offset =
default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)];
if (!compile_app_image_) {
native_object_relocations_.emplace(&image_method_array_,
- NativeObjectRelocation { default_oat_filename_, offset, image_method_type });
+ NativeObjectRelocation { GetDefaultOatIndex(), offset, image_method_type });
}
size_t method_alignment = ArtMethod::Alignment(target_ptr_size_);
const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize(
@@ -1350,15 +1273,14 @@ void ImageWriter::CalculateNewObjectOffsets() {
CHECK(m->IsRuntimeMethod());
DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image";
if (!IsInBootImage(m)) {
- AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, default_oat_filename_);
+ AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, GetDefaultOatIndex());
}
}
// Calculate size of the dex cache arrays slot and prepare offsets.
PrepareDexCacheArraySlots();
// Calculate the sizes of the intern tables and class tables.
- for (const char* oat_filename : oat_filenames_) {
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ for (ImageInfo& image_info : image_infos_) {
// Calculate how big the intern table will be after being serialized.
InternTable* const intern_table = image_info.intern_table_.get();
CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings";
@@ -1369,8 +1291,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
}
// Calculate bin slot offsets.
- for (const char* oat_filename : oat_filenames_) {
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ for (ImageInfo& image_info : image_infos_) {
size_t bin_offset = image_objects_offset_begin_;
for (size_t i = 0; i != kBinSize; ++i) {
image_info.bin_slot_offsets_[i] = bin_offset;
@@ -1390,8 +1311,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
// Calculate image offsets.
size_t image_offset = 0;
- for (const char* oat_filename : oat_filenames_) {
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ for (ImageInfo& image_info : image_infos_) {
image_info.image_begin_ = global_image_begin_ + image_offset;
image_info.image_offset_ = image_offset;
ImageSection unused_sections[ImageHeader::kSectionCount];
@@ -1408,8 +1328,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
// DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_);
size_t i = 0;
- for (const char* oat_filename : oat_filenames_) {
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ for (ImageInfo& image_info : image_infos_) {
image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get()));
i++;
}
@@ -1418,7 +1337,7 @@ void ImageWriter::CalculateNewObjectOffsets() {
for (auto& pair : native_object_relocations_) {
NativeObjectRelocation& relocation = pair.second;
Bin bin_type = BinTypeForNativeRelocationType(relocation.type);
- ImageInfo& image_info = GetImageInfo(relocation.oat_filename);
+ ImageInfo& image_info = GetImageInfo(relocation.oat_index);
relocation.offset += image_info.bin_slot_offsets_[bin_type];
}
@@ -1467,15 +1386,11 @@ size_t ImageWriter::ImageInfo::CreateImageSections(size_t target_ptr_size,
return cur_pos;
}
-void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
- CHECK_NE(0U, oat_loaded_size);
- const char* oat_filename = oat_file_->GetLocation().c_str();
- ImageInfo& image_info = GetImageInfo(oat_filename);
- const uint8_t* oat_file_begin = GetOatFileBegin(oat_filename);
- const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size;
- image_info.oat_data_begin_ = const_cast<uint8_t*>(oat_file_begin) + oat_data_offset;
- const uint8_t* oat_data_end = image_info.oat_data_begin_ + oat_file_->Size();
- image_info.oat_size_ = oat_file_->Size();
+void ImageWriter::CreateHeader(size_t oat_index) {
+ ImageInfo& image_info = GetImageInfo(oat_index);
+ const uint8_t* oat_file_begin = image_info.oat_file_begin_;
+ const uint8_t* oat_file_end = oat_file_begin + image_info.oat_loaded_size_;
+ const uint8_t* oat_data_end = image_info.oat_data_begin_ + image_info.oat_size_;
// Create the image sections.
ImageSection sections[ImageHeader::kSectionCount];
@@ -1486,7 +1401,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
auto* bitmap_section = &sections[ImageHeader::kSectionImageBitmap];
*bitmap_section = ImageSection(RoundUp(image_end, kPageSize), RoundUp(bitmap_bytes, kPageSize));
if (VLOG_IS_ON(compiler)) {
- LOG(INFO) << "Creating header for " << oat_filename;
+ LOG(INFO) << "Creating header for " << oat_filenames_[oat_index];
size_t idx = 0;
for (const ImageSection& section : sections) {
LOG(INFO) << static_cast<ImageHeader::ImageSections>(idx) << " " << section;
@@ -1515,7 +1430,7 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
image_end,
sections,
image_info.image_roots_address_,
- oat_file_->GetOatHeader().GetChecksum(),
+ image_info.oat_checksum_,
PointerToLowMemUInt32(oat_file_begin),
PointerToLowMemUInt32(image_info.oat_data_begin_),
PointerToLowMemUInt32(oat_data_end),
@@ -1534,8 +1449,8 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) {
ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) {
auto it = native_object_relocations_.find(method);
CHECK(it != native_object_relocations_.end()) << PrettyMethod(method) << " @ " << method;
- const char* oat_filename = GetOatFilename(method->GetDexCache());
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(method->GetDexCache());
+ ImageInfo& image_info = GetImageInfo(oat_index);
CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects";
return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset);
}
@@ -1564,14 +1479,13 @@ class FixupRootVisitor : public RootVisitor {
ImageWriter* const image_writer_;
};
-void ImageWriter::CopyAndFixupNativeData() {
- const char* oat_filename = oat_file_->GetLocation().c_str();
- ImageInfo& image_info = GetImageInfo(oat_filename);
+void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
+ ImageInfo& image_info = GetImageInfo(oat_index);
// Copy ArtFields and methods to their locations and update the array for convenience.
for (auto& pair : native_object_relocations_) {
NativeObjectRelocation& relocation = pair.second;
// Only work with fields and methods that are in the current oat file.
- if (strcmp(relocation.oat_filename, oat_filename) != 0) {
+ if (relocation.oat_index != oat_index) {
continue;
}
auto* dest = image_info.image_->Begin() + relocation.offset;
@@ -1617,7 +1531,7 @@ void ImageWriter::CopyAndFixupNativeData() {
ArtMethod* method = image_methods_[i];
CHECK(method != nullptr);
// Only place runtime methods in the image of the default oat file.
- if (method->IsRuntimeMethod() && strcmp(default_oat_filename_, oat_filename) != 0) {
+ if (method->IsRuntimeMethod() && oat_index != GetDefaultOatIndex()) {
continue;
}
if (!IsInBootImage(method)) {
@@ -1722,7 +1636,7 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a
}
UNREACHABLE();
} else {
- ImageInfo& image_info = GetImageInfo(it->second.oat_filename);
+ ImageInfo& image_info = GetImageInfo(it->second.oat_index);
elem = image_info.image_begin_ + it->second.offset;
}
}
@@ -1735,8 +1649,8 @@ void ImageWriter::CopyAndFixupObject(Object* obj) {
return;
}
size_t offset = GetImageOffset(obj);
- const char* oat_filename = GetOatFilename(obj);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(obj);
+ ImageInfo& image_info = GetImageInfo(oat_index);
auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset);
DCHECK_LT(offset, image_info.image_end_);
const auto* src = reinterpret_cast<const uint8_t*>(obj);
@@ -1820,12 +1734,16 @@ uintptr_t ImageWriter::NativeOffsetInImage(void* obj) {
}
template <typename T>
-T* ImageWriter::NativeLocationInImage(T* obj, const char* oat_filename) {
+T* ImageWriter::NativeLocationInImage(T* obj) {
if (obj == nullptr || IsInBootImage(obj)) {
return obj;
} else {
- ImageInfo& image_info = GetImageInfo(oat_filename);
- return reinterpret_cast<T*>(image_info.image_begin_ + NativeOffsetInImage(obj));
+ auto it = native_object_relocations_.find(obj);
+ CHECK(it != native_object_relocations_.end()) << obj << " spaces "
+ << Runtime::Current()->GetHeap()->DumpSpaces();
+ const NativeObjectRelocation& relocation = it->second;
+ ImageInfo& image_info = GetImageInfo(relocation.oat_index);
+ return reinterpret_cast<T*>(image_info.image_begin_ + relocation.offset);
}
}
@@ -1834,41 +1752,27 @@ T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) {
if (obj == nullptr || IsInBootImage(obj)) {
return obj;
} else {
- const char* oat_filename = GetOatFilenameForDexCache(dex_cache);
- ImageInfo& image_info = GetImageInfo(oat_filename);
+ size_t oat_index = GetOatIndexForDexCache(dex_cache);
+ ImageInfo& image_info = GetImageInfo(oat_index);
return reinterpret_cast<T*>(image_info.image_->Begin() + NativeOffsetInImage(obj));
}
}
class NativeLocationVisitor {
public:
- explicit NativeLocationVisitor(ImageWriter* image_writer, const char* oat_filename)
- : image_writer_(image_writer), oat_filename_(oat_filename) {}
+ explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
template <typename T>
T* operator()(T* ptr) const SHARED_REQUIRES(Locks::mutator_lock_) {
- return image_writer_->NativeLocationInImage(ptr, oat_filename_);
- }
-
- ArtMethod* operator()(ArtMethod* method) const SHARED_REQUIRES(Locks::mutator_lock_) {
- const char* oat_filename = method->IsRuntimeMethod() ? image_writer_->GetDefaultOatFilename() :
- image_writer_->GetOatFilenameForDexCache(method->GetDexCache());
- return image_writer_->NativeLocationInImage(method, oat_filename);
- }
-
- ArtField* operator()(ArtField* field) const SHARED_REQUIRES(Locks::mutator_lock_) {
- const char* oat_filename = image_writer_->GetOatFilenameForDexCache(field->GetDexCache());
- return image_writer_->NativeLocationInImage(field, oat_filename);
+ return image_writer_->NativeLocationInImage(ptr);
}
private:
ImageWriter* const image_writer_;
- const char* oat_filename_;
};
void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
- const char* oat_filename = GetOatFilename(orig);
- orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
+ orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this));
FixupClassVisitor visitor(this, copy);
static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
@@ -1952,11 +1856,10 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
// 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
// done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
// static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
- const char* oat_filename = GetOatFilenameForDexCache(orig_dex_cache);
GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
if (orig_strings != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(),
- NativeLocationInImage(orig_strings, oat_filename),
+ NativeLocationInImage(orig_strings),
/*pointer size*/8u);
orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
ImageAddressVisitor(this));
@@ -1964,7 +1867,7 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
if (orig_types != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
- NativeLocationInImage(orig_types, oat_filename),
+ NativeLocationInImage(orig_types),
/*pointer size*/8u);
orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache),
ImageAddressVisitor(this));
@@ -1972,32 +1875,25 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
if (orig_methods != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(),
- NativeLocationInImage(orig_methods, oat_filename),
+ NativeLocationInImage(orig_methods),
/*pointer size*/8u);
ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
- const char* method_oat_filename;
- if (orig == nullptr || orig->IsRuntimeMethod()) {
- method_oat_filename = default_oat_filename_;
- } else {
- method_oat_filename = GetOatFilenameForDexCache(orig->GetDexCache());
- }
- ArtMethod* copy = NativeLocationInImage(orig, method_oat_filename);
+ // NativeLocationInImage also handles runtime methods since these have relocation info.
+ ArtMethod* copy = NativeLocationInImage(orig);
mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
}
}
ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
if (orig_fields != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(),
- NativeLocationInImage(orig_fields, oat_filename),
+ NativeLocationInImage(orig_fields),
/*pointer size*/8u);
ArtField** copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
- const char* field_oat_filename =
- orig == nullptr ? default_oat_filename_ : GetOatFilenameForDexCache(orig->GetDexCache());
- ArtField* copy = NativeLocationInImage(orig, field_oat_filename);
+ ArtField* copy = NativeLocationInImage(orig);
mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
@@ -2055,9 +1951,19 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method,
// trampoline.
// Quick entrypoint:
- uint32_t quick_oat_code_offset = PointerToLowMemUInt32(
- method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_));
- const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
+ const void* quick_oat_entry_point =
+ method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_);
+ const uint8_t* quick_code;
+
+ if (UNLIKELY(IsInBootImage(method->GetDeclaringClass()))) {
+ DCHECK(method->IsCopied());
+ // If the code is not in the oat file corresponding to this image (e.g. default methods)
+ quick_code = reinterpret_cast<const uint8_t*>(quick_oat_entry_point);
+ } else {
+ uint32_t quick_oat_code_offset = PointerToLowMemUInt32(quick_oat_entry_point);
+ quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
+ }
+
*quick_is_interpreted = false;
if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() ||
method->GetDeclaringClass()->IsInitialized())) {
@@ -2089,20 +1995,10 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
- const char* oat_filename;
- if (orig->IsRuntimeMethod() || compile_app_image_) {
- oat_filename = default_oat_filename_;
- } else {
- auto it = dex_file_oat_filename_map_.find(orig->GetDexFile());
- DCHECK(it != dex_file_oat_filename_map_.end()) << orig->GetDexFile()->GetLocation();
- oat_filename = it->second;
- }
ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
- copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods, oat_filename),
- target_ptr_size_);
+ copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
- copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types, oat_filename),
- target_ptr_size_);
+ copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
@@ -2150,34 +2046,6 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig,
}
}
-static OatHeader* GetOatHeaderFromElf(ElfFile* elf) {
- uint64_t data_sec_offset;
- bool has_data_sec = elf->GetSectionOffsetAndSize(".rodata", &data_sec_offset, nullptr);
- if (!has_data_sec) {
- return nullptr;
- }
- return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec_offset);
-}
-
-void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) {
- std::string error_msg;
- std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file,
- PROT_READ | PROT_WRITE,
- MAP_SHARED,
- &error_msg));
- if (elf.get() == nullptr) {
- LOG(FATAL) << "Unable open oat file: " << error_msg;
- return;
- }
- OatHeader* oat_header = GetOatHeaderFromElf(elf.get());
- CHECK(oat_header != nullptr);
- CHECK(oat_header->IsValid());
-
- ImageInfo& image_info = GetImageInfo(oat_file_->GetLocation().c_str());
- ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin());
- image_header->SetOatChecksum(oat_header->GetChecksum());
-}
-
size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const {
DCHECK_LE(up_to, kBinSize);
return std::accumulate(&image_info.bin_slot_sizes_[0],
@@ -2208,19 +2076,6 @@ uint32_t ImageWriter::BinSlot::GetIndex() const {
return lockword_ & ~kBinMask;
}
-uint8_t* ImageWriter::GetOatFileBegin(const char* oat_filename) const {
- uintptr_t last_image_end = 0;
- for (const char* oat_fn : oat_filenames_) {
- const ImageInfo& image_info = GetConstImageInfo(oat_fn);
- DCHECK(image_info.image_begin_ != nullptr);
- uintptr_t this_end = reinterpret_cast<uintptr_t>(image_info.image_begin_) +
- image_info.image_size_;
- last_image_end = std::max(this_end, last_image_end);
- }
- const ImageInfo& image_info = GetConstImageInfo(oat_filename);
- return reinterpret_cast<uint8_t*>(last_image_end) + image_info.oat_offset_;
-}
-
ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) {
switch (type) {
case kNativeObjectRelocationTypeArtField:
@@ -2238,92 +2093,113 @@ ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocat
UNREACHABLE();
}
-const char* ImageWriter::GetOatFilename(mirror::Object* obj) const {
+size_t ImageWriter::GetOatIndex(mirror::Object* obj) const {
if (compile_app_image_) {
- return default_oat_filename_;
+ return GetDefaultOatIndex();
} else {
- return GetOatFilenameForDexCache(obj->IsDexCache() ? obj->AsDexCache() :
- obj->IsClass() ? obj->AsClass()->GetDexCache() : obj->GetClass()->GetDexCache());
+ mirror::DexCache* dex_cache =
+ obj->IsDexCache() ? obj->AsDexCache()
+ : obj->IsClass() ? obj->AsClass()->GetDexCache()
+ : obj->GetClass()->GetDexCache();
+ return GetOatIndexForDexCache(dex_cache);
}
}
-const char* ImageWriter::GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const {
- if (compile_app_image_ || dex_cache == nullptr) {
- return default_oat_filename_;
+size_t ImageWriter::GetOatIndexForDexFile(const DexFile* dex_file) const {
+ if (compile_app_image_) {
+ return GetDefaultOatIndex();
} else {
- auto it = dex_file_oat_filename_map_.find(dex_cache->GetDexFile());
- DCHECK(it != dex_file_oat_filename_map_.end()) << dex_cache->GetDexFile()->GetLocation();
+ auto it = dex_file_oat_index_map_.find(dex_file);
+ DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation();
return it->second;
}
}
-ImageWriter::ImageInfo& ImageWriter::GetImageInfo(const char* oat_filename) {
- auto it = image_info_map_.find(oat_filename);
- DCHECK(it != image_info_map_.end());
- return it->second;
+size_t ImageWriter::GetOatIndexForDexCache(mirror::DexCache* dex_cache) const {
+ if (dex_cache == nullptr) {
+ return GetDefaultOatIndex();
+ } else {
+ return GetOatIndexForDexFile(dex_cache->GetDexFile());
+ }
}
-const ImageWriter::ImageInfo& ImageWriter::GetConstImageInfo(const char* oat_filename) const {
- auto it = image_info_map_.find(oat_filename);
- DCHECK(it != image_info_map_.end());
- return it->second;
-}
+void ImageWriter::UpdateOatFileLayout(size_t oat_index,
+ size_t oat_loaded_size,
+ size_t oat_data_offset,
+ size_t oat_data_size) {
+ const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_;
+ for (const ImageInfo& info : image_infos_) {
+ DCHECK_LE(info.image_begin_ + info.image_size_, images_end);
+ }
+ DCHECK(images_end != nullptr); // Image space must be ready.
-const ImageWriter::ImageInfo& ImageWriter::GetImageInfo(size_t index) const {
- DCHECK_LT(index, oat_filenames_.size());
- return GetConstImageInfo(oat_filenames_[index]);
-}
+ ImageInfo& cur_image_info = GetImageInfo(oat_index);
+ cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_;
+ cur_image_info.oat_loaded_size_ = oat_loaded_size;
+ cur_image_info.oat_data_begin_ = cur_image_info.oat_file_begin_ + oat_data_offset;
+ cur_image_info.oat_size_ = oat_data_size;
-void ImageWriter::UpdateOatFile(File* oat_file, const char* oat_filename) {
- DCHECK(oat_file != nullptr);
if (compile_app_image_) {
CHECK_EQ(oat_filenames_.size(), 1u) << "App image should have no next image.";
return;
}
- ImageInfo& cur_image_info = GetImageInfo(oat_filename);
// Update the oat_offset of the next image info.
- auto it = std::find(oat_filenames_.begin(), oat_filenames_.end(), oat_filename);
- DCHECK(it != oat_filenames_.end());
-
- it++;
- if (it != oat_filenames_.end()) {
- size_t oat_loaded_size = 0;
- size_t oat_data_offset = 0;
- ElfWriter::GetOatElfInformation(oat_file, &oat_loaded_size, &oat_data_offset);
+ if (oat_index + 1u != oat_filenames_.size()) {
// There is a following one.
- ImageInfo& next_image_info = GetImageInfo(*it);
+ ImageInfo& next_image_info = GetImageInfo(oat_index + 1u);
next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size;
}
}
+void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header) {
+ ImageInfo& cur_image_info = GetImageInfo(oat_index);
+ cur_image_info.oat_checksum_ = oat_header.GetChecksum();
+
+ if (oat_index == GetDefaultOatIndex()) {
+ // Primary oat file, read the trampolines.
+ cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] =
+ oat_header.GetInterpreterToInterpreterBridgeOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] =
+ oat_header.GetInterpreterToCompiledCodeBridgeOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] =
+ oat_header.GetJniDlsymLookupOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] =
+ oat_header.GetQuickGenericJniTrampolineOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] =
+ oat_header.GetQuickImtConflictTrampolineOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] =
+ oat_header.GetQuickResolutionTrampolineOffset();
+ cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] =
+ oat_header.GetQuickToInterpreterBridgeOffset();
+ }
+}
+
ImageWriter::ImageWriter(
const CompilerDriver& compiler_driver,
uintptr_t image_begin,
bool compile_pic,
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
- const std::vector<const char*> oat_filenames,
- const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map)
+ const std::vector<const char*>& oat_filenames,
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map)
: compiler_driver_(compiler_driver),
global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
- oat_file_(nullptr),
compile_pic_(compile_pic),
compile_app_image_(compile_app_image),
target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())),
+ image_infos_(oat_filenames.size()),
image_method_array_(ImageHeader::kImageMethodsCount),
dirty_methods_(0u),
clean_methods_(0u),
image_storage_mode_(image_storage_mode),
- dex_file_oat_filename_map_(dex_file_oat_filename_map),
oat_filenames_(oat_filenames),
- default_oat_filename_(oat_filenames[0]) {
+ dex_file_oat_index_map_(dex_file_oat_index_map) {
CHECK_NE(image_begin, 0U);
- for (const char* oat_filename : oat_filenames) {
- image_info_map_.emplace(oat_filename, ImageInfo());
- }
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
+ CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
+ << "Compiling a boot image should occur iff there are no boot image spaces loaded";
}
ImageWriter::ImageInfo::ImageInfo()
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 9371d9ffa9..dba9dd71fc 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -27,6 +27,7 @@
#include <ostream>
#include "base/bit_utils.h"
+#include "base/dchecked_vector.h"
#include "base/length_prefixed_array.h"
#include "base/macros.h"
#include "driver/compiler_driver.h"
@@ -59,20 +60,19 @@ class ImageWriter FINAL {
bool compile_pic,
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
- const std::vector<const char*> oat_filenames,
- const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map);
+ const std::vector<const char*>& oat_filenames,
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map);
bool PrepareImageAddressSpace();
bool IsImageAddressSpaceReady() const {
- bool ready = !image_info_map_.empty();
- for (auto& pair : image_info_map_) {
- const ImageInfo& image_info = pair.second;
+ DCHECK(!image_infos_.empty());
+ for (const ImageInfo& image_info : image_infos_) {
if (image_info.image_roots_address_ == 0u) {
return false;
}
}
- return ready;
+ return true;
}
template <typename T>
@@ -80,8 +80,8 @@ class ImageWriter FINAL {
if (object == nullptr || IsInBootImage(object)) {
return object;
} else {
- const char* oat_filename = GetOatFilename(object);
- const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object));
}
}
@@ -91,9 +91,9 @@ class ImageWriter FINAL {
template <typename PtrType>
PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset)
const SHARED_REQUIRES(Locks::mutator_lock_) {
- auto oat_it = dex_file_oat_filename_map_.find(dex_file);
- DCHECK(oat_it != dex_file_oat_filename_map_.end());
- const ImageInfo& image_info = GetConstImageInfo(oat_it->second);
+ auto oat_it = dex_file_oat_index_map_.find(dex_file);
+ DCHECK(oat_it != dex_file_oat_index_map_.end());
+ const ImageInfo& image_info = GetImageInfo(oat_it->second);
auto it = image_info.dex_cache_array_starts_.find(dex_file);
DCHECK(it != image_info.dex_cache_array_starts_.end());
return reinterpret_cast<PtrType>(
@@ -101,7 +101,13 @@ class ImageWriter FINAL {
it->second + offset);
}
- uint8_t* GetOatFileBegin(const char* oat_filename) const;
+ size_t GetOatFileOffset(size_t oat_index) const {
+ return GetImageInfo(oat_index).oat_offset_;
+ }
+
+ const uint8_t* GetOatFileBegin(size_t oat_index) const {
+ return GetImageInfo(oat_index).oat_file_begin_;
+ }
// If image_fd is not kInvalidFd, then we use that for the image file. Otherwise we open
// the names in image_filenames.
@@ -109,21 +115,32 @@ class ImageWriter FINAL {
// the names in oat_filenames.
bool Write(int image_fd,
const std::vector<const char*>& image_filenames,
- int oat_fd,
- const std::vector<const char*>& oat_filenames,
- const std::string& oat_location)
+ const std::vector<const char*>& oat_filenames)
REQUIRES(!Locks::mutator_lock_);
- uintptr_t GetOatDataBegin(const char* oat_filename) {
- return reinterpret_cast<uintptr_t>(GetImageInfo(oat_filename).oat_data_begin_);
+ uintptr_t GetOatDataBegin(size_t oat_index) {
+ return reinterpret_cast<uintptr_t>(GetImageInfo(oat_index).oat_data_begin_);
}
- const char* GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const
+ // Get the index of the oat file containing the dex file.
+ //
+ // This "oat_index" is used to retrieve information about the the memory layout
+ // of the oat file and its associated image file, needed for link-time patching
+ // of references to the image or across oat files.
+ size_t GetOatIndexForDexFile(const DexFile* dex_file) const;
+
+ // Get the index of the oat file containing the dex file served by the dex cache.
+ size_t GetOatIndexForDexCache(mirror::DexCache* dex_cache) const
SHARED_REQUIRES(Locks::mutator_lock_);
- // Update the oat size for the given oat file. This will make the oat_offset for the next oat
- // file valid.
- void UpdateOatFile(File* oat_file, const char* oat_filename);
+ // Update the oat layout for the given oat file.
+ // This will make the oat_offset for the next oat file valid.
+ void UpdateOatFileLayout(size_t oat_index,
+ size_t oat_loaded_size,
+ size_t oat_data_offset,
+ size_t oat_data_size);
+ // Update information about the oat header, i.e. checksum and trampoline offsets.
+ void UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_header);
private:
bool AllocMemory();
@@ -247,10 +264,13 @@ class ImageWriter FINAL {
// Offset of the oat file for this image from start of oat files. This is
// valid when the previous oat file has been written.
size_t oat_offset_ = 0;
- // Start of oatdata in the corresponding oat file. This is
- // valid when the images have been layed out.
- uint8_t* oat_data_begin_ = nullptr;
+ // Layout of the loaded ELF file containing the oat file, valid after UpdateOatFileLayout().
+ const uint8_t* oat_file_begin_ = nullptr;
+ size_t oat_loaded_size_ = 0;
+ const uint8_t* oat_data_begin_ = nullptr;
size_t oat_size_ = 0; // Size of the corresponding oat data.
+ // The oat header checksum, valid after UpdateOatFileHeader().
+ uint32_t oat_checksum_ = 0u;
// Image bitmap which lets us know where the objects inside of the image reside.
std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_;
@@ -310,8 +330,8 @@ class ImageWriter FINAL {
mirror::Object* GetLocalAddress(mirror::Object* object) const
SHARED_REQUIRES(Locks::mutator_lock_) {
size_t offset = GetImageOffset(object);
- const char* oat_filename = GetOatFilename(object);
- const ImageInfo& image_info = GetConstImageInfo(oat_filename);
+ size_t oat_index = GetOatIndex(object);
+ const ImageInfo& image_info = GetImageInfo(oat_index);
uint8_t* dst = image_info.image_->Begin() + offset;
return reinterpret_cast<mirror::Object*>(dst);
}
@@ -348,9 +368,9 @@ class ImageWriter FINAL {
// Lays out where the image objects will be at runtime.
void CalculateNewObjectOffsets()
SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset)
+ void CreateHeader(size_t oat_index)
SHARED_REQUIRES(Locks::mutator_lock_);
- mirror::ObjectArray<mirror::Object>* CreateImageRoots(const char* oat_filename) const
+ mirror::ObjectArray<mirror::Object>* CreateImageRoots(size_t oat_index) const
SHARED_REQUIRES(Locks::mutator_lock_);
void CalculateObjectBinSlots(mirror::Object* obj)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -367,7 +387,7 @@ class ImageWriter FINAL {
SHARED_REQUIRES(Locks::mutator_lock_);
// Creates the contiguous image in memory and adjusts pointers.
- void CopyAndFixupNativeData() SHARED_REQUIRES(Locks::mutator_lock_);
+ void CopyAndFixupNativeData(size_t oat_index) SHARED_REQUIRES(Locks::mutator_lock_);
void CopyAndFixupObjects() SHARED_REQUIRES(Locks::mutator_lock_);
static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -392,9 +412,6 @@ class ImageWriter FINAL {
bool* quick_is_interpreted)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Patches references in OatFile to expect runtime addresses.
- void SetOatChecksumFromElfFile(File* elf_file);
-
// Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins.
size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const;
@@ -404,22 +421,24 @@ class ImageWriter FINAL {
// Assign the offset for an ArtMethod.
void AssignMethodOffset(ArtMethod* method,
NativeObjectRelocationType type,
- const char* oat_filename)
+ size_t oat_index)
SHARED_REQUIRES(Locks::mutator_lock_);
// Return true if klass is loaded by the boot class loader but not in the boot image.
bool IsBootClassLoaderNonImageClass(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_);
- // Return true if klass depends on a boot class loader non image class live. We want to prune
- // these classes since we do not want any boot class loader classes in the image. This means that
+ // Return true if klass depends on a boot class loader non image class. We want to prune these
+ // classes since we do not want any boot class loader classes in the image. This means that
// we also cannot have any classes which refer to these boot class loader non image classes.
- bool ContainsBootClassLoaderNonImageClass(mirror::Class* klass)
+ // PruneAppImageClass also prunes if klass depends on a non-image class according to the compiler
+ // driver.
+ bool PruneAppImageClass(mirror::Class* klass)
SHARED_REQUIRES(Locks::mutator_lock_);
// early_exit is true if we had a cyclic dependency anywhere down the chain.
- bool ContainsBootClassLoaderNonImageClassInternal(mirror::Class* klass,
- bool* early_exit,
- std::unordered_set<mirror::Class*>* visited)
+ bool PruneAppImageClassInternal(mirror::Class* klass,
+ bool* early_exit,
+ std::unordered_set<mirror::Class*>* visited)
SHARED_REQUIRES(Locks::mutator_lock_);
static Bin BinTypeForNativeRelocationType(NativeObjectRelocationType type);
@@ -428,7 +447,7 @@ class ImageWriter FINAL {
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
- T* NativeLocationInImage(T* obj, const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_);
+ T* NativeLocationInImage(T* obj) SHARED_REQUIRES(Locks::mutator_lock_);
// Location of where the temporary copy of the object currently is.
template <typename T>
@@ -441,15 +460,21 @@ class ImageWriter FINAL {
// Return true if ptr is within the boot oat file.
bool IsInBootOatFile(const void* ptr) const;
- const char* GetOatFilename(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
+ // Get the index of the oat file associated with the object.
+ size_t GetOatIndex(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_);
- const char* GetDefaultOatFilename() const {
- return default_oat_filename_;
+ // The oat index for shared data in multi-image and all data in single-image compilation.
+ size_t GetDefaultOatIndex() const {
+ return 0u;
}
- ImageInfo& GetImageInfo(const char* oat_filename);
- const ImageInfo& GetConstImageInfo(const char* oat_filename) const;
- const ImageInfo& GetImageInfo(size_t index) const;
+ ImageInfo& GetImageInfo(size_t oat_index) {
+ return image_infos_[oat_index];
+ }
+
+ const ImageInfo& GetImageInfo(size_t oat_index) const {
+ return image_infos_[oat_index];
+ }
// Find an already strong interned string in the other images or in the boot image. Used to
// remove duplicates in the multi image and app image case.
@@ -463,9 +488,6 @@ class ImageWriter FINAL {
// Offset from image_begin_ to where the first object is in image_.
size_t image_objects_offset_begin_;
- // oat file with code for this image
- OatFile* oat_file_;
-
// Pointer arrays that need to be updated. Since these are only some int and long arrays, we need
// to keep track. These include vtable arrays, iftable arrays, and dex caches.
std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_;
@@ -481,14 +503,14 @@ class ImageWriter FINAL {
// Size of pointers on the target architecture.
size_t target_ptr_size_;
- // Mapping of oat filename to image data.
- std::unordered_map<std::string, ImageInfo> image_info_map_;
+ // Image data indexed by the oat file index.
+ dchecked_vector<ImageInfo> image_infos_;
// ArtField, ArtMethod relocating map. These are allocated as array of structs but we want to
// have one entry per art field for convenience. ArtFields are placed right after the end of the
// image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields.
struct NativeObjectRelocation {
- const char* oat_filename;
+ size_t oat_index;
uintptr_t offset;
NativeObjectRelocationType type;
@@ -520,10 +542,11 @@ class ImageWriter FINAL {
// Which mode the image is stored as, see image.h
const ImageHeader::StorageMode image_storage_mode_;
- // Map of dex files to the oat filenames that they were compiled into.
- const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map_;
- const std::vector<const char*> oat_filenames_;
- const char* default_oat_filename_;
+ // The file names of oat files.
+ const std::vector<const char*>& oat_filenames_;
+
+ // Map of dex files to the indexes of oat files that they were compiled into.
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
friend class ContainsBootClassLoaderNonImageClassVisitor;
friend class FixupClassVisitor;
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 3fe786141e..23601c39e4 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -23,10 +23,7 @@
#include "base/time_utils.h"
#include "base/timing_logger.h"
#include "base/unix_file/fd_file.h"
-#include "compiler_callbacks.h"
#include "debug/elf_debug_writer.h"
-#include "dex/pass_manager.h"
-#include "dex/quick_compiler_callbacks.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "jit/debugger_interface.h"
@@ -36,7 +33,6 @@
#include "oat_quick_method_header.h"
#include "object_lock.h"
#include "thread_list.h"
-#include "verifier/method_verifier-inl.h"
namespace art {
namespace jit {
@@ -45,11 +41,10 @@ JitCompiler* JitCompiler::Create() {
return new JitCompiler();
}
-extern "C" void* jit_load(CompilerCallbacks** callbacks, bool* generate_debug_info) {
+extern "C" void* jit_load(bool* generate_debug_info) {
VLOG(jit) << "loading jit compiler";
auto* const jit_compiler = JitCompiler::Create();
CHECK(jit_compiler != nullptr);
- *callbacks = jit_compiler->GetCompilerCallbacks();
*generate_debug_info = jit_compiler->GetCompilerOptions()->GetGenerateDebugInfo();
VLOG(jit) << "Done loading jit compiler";
return jit_compiler;
@@ -151,14 +146,10 @@ JitCompiler::JitCompiler() : total_time_(0) {
instruction_set_features_.reset(InstructionSetFeatures::FromCppDefines());
}
cumulative_logger_.reset(new CumulativeLogger("jit times"));
- verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap);
- callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
- method_inliner_map_.get(),
- CompilerCallbacks::CallbackMode::kCompileApp));
compiler_driver_.reset(new CompilerDriver(
compiler_options_.get(),
- verification_results_.get(),
+ /* verification_results */ nullptr,
method_inliner_map_.get(),
Compiler::kOptimizing,
instruction_set,
@@ -172,7 +163,6 @@ JitCompiler::JitCompiler() : total_time_(0) {
/* dump_passes */ false,
cumulative_logger_.get(),
/* swap_fd */ -1,
- /* dex to oat map */ nullptr,
/* profile_compilation_info */ nullptr));
// Disable dedupe so we can remove compiled methods.
compiler_driver_->SetDedupeEnabled(false);
@@ -251,9 +241,5 @@ bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) {
return success;
}
-CompilerCallbacks* JitCompiler::GetCompilerCallbacks() const {
- return callbacks_.get();
-}
-
} // namespace jit
} // namespace art
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index 73b0facf4b..682b008219 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -40,6 +40,11 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceEnd(uint32_t offset) {
MethodReference(nullptr, 0u),
aligned_offset);
if (needs_thunk) {
+ // All remaining patches will be handled by this thunk.
+ DCHECK(!unprocessed_patches_.empty());
+ DCHECK_LE(aligned_offset - unprocessed_patches_.front().second, max_positive_displacement_);
+ unprocessed_patches_.clear();
+
thunk_locations_.push_back(aligned_offset);
offset = CompiledMethod::AlignCode(aligned_offset + thunk_code_.size(), instruction_set_);
}
diff --git a/compiler/linker/arm/relative_patcher_arm_base.h b/compiler/linker/arm/relative_patcher_arm_base.h
index f80dd962ce..25fd35e1d6 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.h
+++ b/compiler/linker/arm/relative_patcher_arm_base.h
@@ -27,18 +27,23 @@ namespace linker {
class ArmBaseRelativePatcher : public RelativePatcher {
public:
- uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method,
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
protected:
ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
- InstructionSet instruction_set, std::vector<uint8_t> thunk_code,
- uint32_t max_positive_displacement, uint32_t max_negative_displacement);
+ InstructionSet instruction_set,
+ std::vector<uint8_t> thunk_code,
+ uint32_t max_positive_displacement,
+ uint32_t max_negative_displacement);
- uint32_t ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method,
- MethodReference method_ref, uint32_t max_extra_space);
+ uint32_t ReserveSpaceInternal(uint32_t offset,
+ const CompiledMethod* compiled_method,
+ MethodReference method_ref,
+ uint32_t max_extra_space);
uint32_t CalculateDisplacement(uint32_t patch_offset, uint32_t target_offset);
private:
diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc
index 5f4f760c14..c090dffc55 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.cc
+++ b/compiler/linker/arm/relative_patcher_thumb2.cc
@@ -28,8 +28,10 @@ Thumb2RelativePatcher::Thumb2RelativePatcher(RelativePatcherTargetProvider* prov
kMaxPositiveDisplacement, kMaxNegativeDisplacement) {
}
-void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) {
+void Thumb2RelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(literal_offset + 4u, code->size());
DCHECK_EQ(literal_offset & 1u, 0u);
DCHECK_EQ(patch_offset & 1u, 0u);
diff --git a/compiler/linker/arm/relative_patcher_thumb2.h b/compiler/linker/arm/relative_patcher_thumb2.h
index 006d6fb9d5..0d903c0b41 100644
--- a/compiler/linker/arm/relative_patcher_thumb2.h
+++ b/compiler/linker/arm/relative_patcher_thumb2.h
@@ -26,10 +26,14 @@ class Thumb2RelativePatcher FINAL : public ArmBaseRelativePatcher {
public:
explicit Thumb2RelativePatcher(RelativePatcherTargetProvider* provider);
- void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
private:
static std::vector<uint8_t> CompileThunkCode();
diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc
index 3d4c2184f1..a81c85c707 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.cc
+++ b/compiler/linker/arm64/relative_patcher_arm64.cc
@@ -131,8 +131,10 @@ uint32_t Arm64RelativePatcher::WriteThunks(OutputStream* out, uint32_t offset) {
return ArmBaseRelativePatcher::WriteThunks(out, offset);
}
-void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) {
+void Arm64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset, uint32_t
+ target_offset) {
DCHECK_LE(literal_offset + 4u, code->size());
DCHECK_EQ(literal_offset & 3u, 0u);
DCHECK_EQ(patch_offset & 3u, 0u);
diff --git a/compiler/linker/arm64/relative_patcher_arm64.h b/compiler/linker/arm64/relative_patcher_arm64.h
index 2d07e75c85..f9b76e6250 100644
--- a/compiler/linker/arm64/relative_patcher_arm64.h
+++ b/compiler/linker/arm64/relative_patcher_arm64.h
@@ -28,14 +28,19 @@ class Arm64RelativePatcher FINAL : public ArmBaseRelativePatcher {
Arm64RelativePatcher(RelativePatcherTargetProvider* provider,
const Arm64InstructionSetFeatures* features);
- uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method,
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
private:
static std::vector<uint8_t> CompileThunkCode();
diff --git a/compiler/linker/multi_oat_relative_patcher.cc b/compiler/linker/multi_oat_relative_patcher.cc
new file mode 100644
index 0000000000..e9e242b658
--- /dev/null
+++ b/compiler/linker/multi_oat_relative_patcher.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "multi_oat_relative_patcher.h"
+
+#include "globals.h"
+#include "base/bit_utils.h"
+#include "base/logging.h"
+
+namespace art {
+namespace linker {
+
+MultiOatRelativePatcher::MultiOatRelativePatcher(InstructionSet instruction_set,
+ const InstructionSetFeatures* features)
+ : method_offset_map_(),
+ relative_patcher_(
+ linker::RelativePatcher::Create(instruction_set, features, &method_offset_map_)),
+ adjustment_(0u),
+ instruction_set_(instruction_set),
+ start_size_code_alignment_(0u),
+ start_size_relative_call_thunks_(0u),
+ start_size_misc_thunks_(0u) {
+}
+
+void MultiOatRelativePatcher::StartOatFile(uint32_t adjustment) {
+ DCHECK_ALIGNED(adjustment, kPageSize);
+ adjustment_ = adjustment;
+
+ start_size_code_alignment_ = relative_patcher_->CodeAlignmentSize();
+ start_size_relative_call_thunks_ = relative_patcher_->RelativeCallThunksSize();
+ start_size_misc_thunks_ = relative_patcher_->MiscThunksSize();
+}
+
+uint32_t MultiOatRelativePatcher::CodeAlignmentSize() const {
+ DCHECK_GE(relative_patcher_->CodeAlignmentSize(), start_size_code_alignment_);
+ return relative_patcher_->CodeAlignmentSize() - start_size_code_alignment_;
+}
+
+uint32_t MultiOatRelativePatcher::RelativeCallThunksSize() const {
+ DCHECK_GE(relative_patcher_->RelativeCallThunksSize(), start_size_relative_call_thunks_);
+ return relative_patcher_->RelativeCallThunksSize() - start_size_relative_call_thunks_;
+}
+
+uint32_t MultiOatRelativePatcher::MiscThunksSize() const {
+ DCHECK_GE(relative_patcher_->MiscThunksSize(), start_size_misc_thunks_);
+ return relative_patcher_->MiscThunksSize() - start_size_misc_thunks_;
+}
+
+std::pair<bool, uint32_t> MultiOatRelativePatcher::MethodOffsetMap::FindMethodOffset(
+ MethodReference ref) {
+ auto it = map.find(ref);
+ if (it == map.end()) {
+ return std::pair<bool, uint32_t>(false, 0u);
+ } else {
+ return std::pair<bool, uint32_t>(true, it->second);
+ }
+}
+} // namespace linker
+} // namespace art
diff --git a/compiler/linker/multi_oat_relative_patcher.h b/compiler/linker/multi_oat_relative_patcher.h
new file mode 100644
index 0000000000..1727d529fc
--- /dev/null
+++ b/compiler/linker/multi_oat_relative_patcher.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
+#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
+
+#include "arch/instruction_set.h"
+#include "method_reference.h"
+#include "relative_patcher.h"
+#include "safe_map.h"
+
+namespace art {
+
+class CompiledMethod;
+class LinkerPatch;
+class InstructionSetFeatures;
+
+namespace linker {
+
+// MultiOatRelativePatcher is a helper class for handling patching across
+// any number of oat files. It provides storage for method code offsets
+// and wraps RelativePatcher calls, adjusting relative offsets according
+// to the value set by SetAdjustment().
+class MultiOatRelativePatcher FINAL {
+ public:
+ using const_iterator =
+ SafeMap<MethodReference, uint32_t, MethodReferenceComparator>::const_iterator;
+
+ MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features);
+
+ // Mark the start of a new oat file (for statistics retrieval) and set the
+ // adjustment for a new oat file to apply to all relative offsets that are
+ // passed to the MultiOatRelativePatcher.
+ //
+ // The adjustment should be the global offset of the base from which relative
+ // offsets are calculated, such as the start of .rodata for the current oat file.
+ // It must must never point directly to a method's code to avoid relative offsets
+ // with value 0 because this value is used as a missing offset indication in
+ // GetOffset() and an error indication in WriteThunks(). Additionally, it must be
+ // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP.
+ void StartOatFile(uint32_t adjustment);
+
+ // Get relative offset. Returns 0 when the offset has not been set yet.
+ uint32_t GetOffset(MethodReference method_ref) {
+ auto it = method_offset_map_.map.find(method_ref);
+ return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u;
+ }
+
+ // Set the offset.
+ void SetOffset(MethodReference method_ref, uint32_t offset) {
+ method_offset_map_.map.Put(method_ref, offset + adjustment_);
+ }
+
+ // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment.
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
+ MethodReference method_ref) {
+ offset += adjustment_;
+ offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref);
+ offset -= adjustment_;
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment.
+ uint32_t ReserveSpaceEnd(uint32_t offset) {
+ offset += adjustment_;
+ offset = relative_patcher_->ReserveSpaceEnd(offset);
+ offset -= adjustment_;
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment.
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) {
+ offset += adjustment_;
+ offset = relative_patcher_->WriteThunks(out, offset);
+ if (offset != 0u) { // 0u indicates write error.
+ offset -= adjustment_;
+ }
+ return offset;
+ }
+
+ // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment.
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ patch_offset += adjustment_;
+ target_offset += adjustment_;
+ relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset);
+ }
+
+ // Wrapper around RelativePatcher::PatchDexCacheReference(), doing offset adjustment.
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
+ patch_offset += adjustment_;
+ target_offset += adjustment_;
+ relative_patcher_->PatchDexCacheReference(code, patch, patch_offset, target_offset);
+ }
+
+ // Wrappers around RelativePatcher for statistics retrieval.
+ uint32_t CodeAlignmentSize() const;
+ uint32_t RelativeCallThunksSize() const;
+ uint32_t MiscThunksSize() const;
+
+ private:
+ // Map method reference to assigned offset.
+ // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
+ class MethodOffsetMap : public linker::RelativePatcherTargetProvider {
+ public:
+ std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE;
+ SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map;
+ };
+
+ MethodOffsetMap method_offset_map_;
+ std::unique_ptr<RelativePatcher> relative_patcher_;
+ uint32_t adjustment_;
+ InstructionSet instruction_set_;
+
+ uint32_t start_size_code_alignment_;
+ uint32_t start_size_relative_call_thunks_;
+ uint32_t start_size_misc_thunks_;
+
+ friend class MultiOatRelativePatcherTest;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
+};
+
+} // namespace linker
+} // namespace art
+
+#endif // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
diff --git a/compiler/linker/multi_oat_relative_patcher_test.cc b/compiler/linker/multi_oat_relative_patcher_test.cc
new file mode 100644
index 0000000000..792cdfe8e9
--- /dev/null
+++ b/compiler/linker/multi_oat_relative_patcher_test.cc
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compiled_method.h"
+#include "gtest/gtest.h"
+#include "multi_oat_relative_patcher.h"
+#include "vector_output_stream.h"
+
+namespace art {
+namespace linker {
+
+static const MethodReference kNullMethodRef = MethodReference(nullptr, 0u);
+
+static bool EqualRef(MethodReference lhs, MethodReference rhs) {
+ return lhs.dex_file == rhs.dex_file && lhs.dex_method_index == rhs.dex_method_index;
+}
+
+class MultiOatRelativePatcherTest : public testing::Test {
+ protected:
+ class MockPatcher : public RelativePatcher {
+ public:
+ MockPatcher() { }
+
+ uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method ATTRIBUTE_UNUSED,
+ MethodReference method_ref) OVERRIDE {
+ last_reserve_offset_ = offset;
+ last_reserve_method_ = method_ref;
+ offset += next_reserve_adjustment_;
+ next_reserve_adjustment_ = 0u;
+ return offset;
+ }
+
+ uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE {
+ last_reserve_offset_ = offset;
+ last_reserve_method_ = kNullMethodRef;
+ offset += next_reserve_adjustment_;
+ next_reserve_adjustment_ = 0u;
+ return offset;
+ }
+
+ uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE {
+ last_write_offset_ = offset;
+ if (next_write_alignment_ != 0u) {
+ offset += next_write_alignment_;
+ bool success = WriteCodeAlignment(out, next_write_alignment_);
+ CHECK(success);
+ next_write_alignment_ = 0u;
+ }
+ if (next_write_call_thunk_ != 0u) {
+ offset += next_write_call_thunk_;
+ std::vector<uint8_t> thunk(next_write_call_thunk_, 'c');
+ bool success = WriteRelCallThunk(out, ArrayRef<const uint8_t>(thunk));
+ CHECK(success);
+ next_write_call_thunk_ = 0u;
+ }
+ if (next_write_misc_thunk_ != 0u) {
+ offset += next_write_misc_thunk_;
+ std::vector<uint8_t> thunk(next_write_misc_thunk_, 'm');
+ bool success = WriteMiscThunk(out, ArrayRef<const uint8_t>(thunk));
+ CHECK(success);
+ next_write_misc_thunk_ = 0u;
+ }
+ return offset;
+ }
+
+ void PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ last_literal_offset_ = literal_offset;
+ last_patch_offset_ = patch_offset;
+ last_target_offset_ = target_offset;
+ }
+
+ void PatchDexCacheReference(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE {
+ last_literal_offset_ = patch.LiteralOffset();
+ last_patch_offset_ = patch_offset;
+ last_target_offset_ = target_offset;
+ }
+
+ uint32_t last_reserve_offset_ = 0u;
+ MethodReference last_reserve_method_ = kNullMethodRef;
+ uint32_t next_reserve_adjustment_ = 0u;
+
+ uint32_t last_write_offset_ = 0u;
+ uint32_t next_write_alignment_ = 0u;
+ uint32_t next_write_call_thunk_ = 0u;
+ uint32_t next_write_misc_thunk_ = 0u;
+
+ uint32_t last_literal_offset_ = 0u;
+ uint32_t last_patch_offset_ = 0u;
+ uint32_t last_target_offset_ = 0u;
+ };
+
+ MultiOatRelativePatcherTest()
+ : instruction_set_features_(InstructionSetFeatures::FromCppDefines()),
+ patcher_(kRuntimeISA, instruction_set_features_.get()) {
+ std::unique_ptr<MockPatcher> mock(new MockPatcher());
+ mock_ = mock.get();
+ patcher_.relative_patcher_ = std::move(mock);
+ }
+
+ std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+ MultiOatRelativePatcher patcher_;
+ MockPatcher* mock_;
+};
+
+TEST_F(MultiOatRelativePatcherTest, Offsets) {
+ const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
+ MethodReference ref1(dex_file, 1u);
+ MethodReference ref2(dex_file, 2u);
+ EXPECT_EQ(0u, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+ EXPECT_EQ(0u, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t off1 = 0x1234;
+ patcher_.SetOffset(ref1, off1);
+ EXPECT_EQ(off1, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment2 = 0x30000;
+ patcher_.StartOatFile(adjustment2);
+ EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
+ EXPECT_EQ(0u, patcher_.GetOffset(ref2));
+
+ uint32_t off2 = 0x4321;
+ patcher_.SetOffset(ref2, off2);
+ EXPECT_EQ(off1 + adjustment1 - adjustment2, patcher_.GetOffset(ref1));
+ EXPECT_EQ(off2, patcher_.GetOffset(ref2));
+
+ uint32_t adjustment3 = 0x78000;
+ patcher_.StartOatFile(adjustment3);
+ EXPECT_EQ(off1 + adjustment1 - adjustment3, patcher_.GetOffset(ref1));
+ EXPECT_EQ(off2 + adjustment2 - adjustment3, patcher_.GetOffset(ref2));
+}
+
+TEST_F(MultiOatRelativePatcherTest, OffsetsInReserve) {
+ const DexFile* dex_file = reinterpret_cast<const DexFile*>(1);
+ MethodReference ref1(dex_file, 1u);
+ MethodReference ref2(dex_file, 2u);
+ MethodReference ref3(dex_file, 3u);
+ const CompiledMethod* method = reinterpret_cast<const CompiledMethod*>(-1);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_offset = 0x100;
+ uint32_t method1_offset_check = patcher_.ReserveSpace(method1_offset, method, ref1);
+ ASSERT_EQ(adjustment1 + method1_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(ref1, mock_->last_reserve_method_));
+ ASSERT_EQ(method1_offset, method1_offset_check);
+
+ uint32_t method2_offset = 0x1230;
+ uint32_t method2_reserve_adjustment = 0x10;
+ mock_->next_reserve_adjustment_ = method2_reserve_adjustment;
+ uint32_t method2_offset_adjusted = patcher_.ReserveSpace(method2_offset, method, ref2);
+ ASSERT_EQ(adjustment1 + method2_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(ref2, mock_->last_reserve_method_));
+ ASSERT_EQ(method2_offset + method2_reserve_adjustment, method2_offset_adjusted);
+
+ uint32_t end1_offset = 0x4320;
+ uint32_t end1_offset_check = patcher_.ReserveSpaceEnd(end1_offset);
+ ASSERT_EQ(adjustment1 + end1_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_));
+ ASSERT_EQ(end1_offset, end1_offset_check);
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_offset = 0xf00;
+ uint32_t method3_offset_check = patcher_.ReserveSpace(method3_offset, method, ref3);
+ ASSERT_EQ(adjustment2 + method3_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(ref3, mock_->last_reserve_method_));
+ ASSERT_EQ(method3_offset, method3_offset_check);
+
+ uint32_t end2_offset = 0x2400;
+ uint32_t end2_reserve_adjustment = 0x20;
+ mock_->next_reserve_adjustment_ = end2_reserve_adjustment;
+ uint32_t end2_offset_adjusted = patcher_.ReserveSpaceEnd(end2_offset);
+ ASSERT_EQ(adjustment2 + end2_offset, mock_->last_reserve_offset_);
+ ASSERT_TRUE(EqualRef(kNullMethodRef, mock_->last_reserve_method_));
+ ASSERT_EQ(end2_offset + end2_reserve_adjustment, end2_offset_adjusted);
+}
+
+TEST_F(MultiOatRelativePatcherTest, Write) {
+ std::vector<uint8_t> output;
+ VectorOutputStream vos("output", &output);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_offset = 0x100;
+ uint32_t method1_offset_check = patcher_.WriteThunks(&vos, method1_offset);
+ ASSERT_EQ(adjustment1 + method1_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method1_offset, method1_offset_check);
+ vos.WriteFully("1", 1); // Mark method1.
+
+ uint32_t method2_offset = 0x1230;
+ uint32_t method2_alignment_size = 1;
+ uint32_t method2_call_thunk_size = 2;
+ mock_->next_write_alignment_ = method2_alignment_size;
+ mock_->next_write_call_thunk_ = method2_call_thunk_size;
+ uint32_t method2_offset_adjusted = patcher_.WriteThunks(&vos, method2_offset);
+ ASSERT_EQ(adjustment1 + method2_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method2_offset + method2_alignment_size + method2_call_thunk_size,
+ method2_offset_adjusted);
+ vos.WriteFully("2", 1); // Mark method2.
+
+ EXPECT_EQ(method2_alignment_size, patcher_.CodeAlignmentSize());
+ EXPECT_EQ(method2_call_thunk_size, patcher_.RelativeCallThunksSize());
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_offset = 0xf00;
+ uint32_t method3_alignment_size = 2;
+ uint32_t method3_misc_thunk_size = 1;
+ mock_->next_write_alignment_ = method3_alignment_size;
+ mock_->next_write_misc_thunk_ = method3_misc_thunk_size;
+ uint32_t method3_offset_adjusted = patcher_.WriteThunks(&vos, method3_offset);
+ ASSERT_EQ(adjustment2 + method3_offset, mock_->last_write_offset_);
+ ASSERT_EQ(method3_offset + method3_alignment_size + method3_misc_thunk_size,
+ method3_offset_adjusted);
+ vos.WriteFully("3", 1); // Mark method3.
+
+ EXPECT_EQ(method3_alignment_size, patcher_.CodeAlignmentSize());
+ EXPECT_EQ(method3_misc_thunk_size, patcher_.MiscThunksSize());
+
+ uint8_t expected_output[] = {
+ '1',
+ 0, 'c', 'c', '2',
+ 0, 0, 'm', '3',
+ };
+ ASSERT_EQ(arraysize(expected_output), output.size());
+ for (size_t i = 0; i != arraysize(expected_output); ++i) {
+ ASSERT_EQ(expected_output[i], output[i]) << i;
+ }
+}
+
+TEST_F(MultiOatRelativePatcherTest, Patch) {
+ std::vector<uint8_t> code(16);
+
+ uint32_t adjustment1 = 0x1000;
+ patcher_.StartOatFile(adjustment1);
+
+ uint32_t method1_literal_offset = 4u;
+ uint32_t method1_patch_offset = 0x1234u;
+ uint32_t method1_target_offset = 0x8888u;
+ patcher_.PatchCall(&code, method1_literal_offset, method1_patch_offset, method1_target_offset);
+ DCHECK_EQ(method1_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method1_patch_offset + adjustment1, mock_->last_patch_offset_);
+ DCHECK_EQ(method1_target_offset + adjustment1, mock_->last_target_offset_);
+
+ uint32_t method2_literal_offset = 12u;
+ uint32_t method2_patch_offset = 0x7654u;
+ uint32_t method2_target_offset = 0xccccu;
+ LinkerPatch method2_patch =
+ LinkerPatch::DexCacheArrayPatch(method2_literal_offset, nullptr, 0u, 1234u);
+ patcher_.PatchDexCacheReference(
+ &code, method2_patch, method2_patch_offset, method2_target_offset);
+ DCHECK_EQ(method2_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method2_patch_offset + adjustment1, mock_->last_patch_offset_);
+ DCHECK_EQ(method2_target_offset + adjustment1, mock_->last_target_offset_);
+
+ uint32_t adjustment2 = 0xd000;
+ patcher_.StartOatFile(adjustment2);
+
+ uint32_t method3_literal_offset = 8u;
+ uint32_t method3_patch_offset = 0x108u;
+ uint32_t method3_target_offset = 0x200u;
+ patcher_.PatchCall(&code, method3_literal_offset, method3_patch_offset, method3_target_offset);
+ DCHECK_EQ(method3_literal_offset, mock_->last_literal_offset_);
+ DCHECK_EQ(method3_patch_offset + adjustment2, mock_->last_patch_offset_);
+ DCHECK_EQ(method3_target_offset + adjustment2, mock_->last_target_offset_);
+}
+
+} // namespace linker
+} // namespace art
diff --git a/compiler/linker/relative_patcher.cc b/compiler/linker/relative_patcher.cc
index 82702dcf25..6727c17583 100644
--- a/compiler/linker/relative_patcher.cc
+++ b/compiler/linker/relative_patcher.cc
@@ -34,7 +34,8 @@ namespace art {
namespace linker {
std::unique_ptr<RelativePatcher> RelativePatcher::Create(
- InstructionSet instruction_set, const InstructionSetFeatures* features,
+ InstructionSet instruction_set,
+ const InstructionSetFeatures* features,
RelativePatcherTargetProvider* provider) {
class RelativePatcherNone FINAL : public RelativePatcher {
public:
diff --git a/compiler/linker/relative_patcher.h b/compiler/linker/relative_patcher.h
index 8a9f3f8364..ba374512a1 100644
--- a/compiler/linker/relative_patcher.h
+++ b/compiler/linker/relative_patcher.h
@@ -83,23 +83,31 @@ class RelativePatcher {
}
// Reserve space for thunks if needed before a method, return adjusted offset.
- virtual uint32_t ReserveSpace(uint32_t offset, const CompiledMethod* compiled_method,
+ virtual uint32_t ReserveSpace(uint32_t offset,
+ const CompiledMethod* compiled_method,
MethodReference method_ref) = 0;
// Reserve space for thunks if needed after the last method, return adjusted offset.
+ // The caller may use this method to preemptively force thunk space reservation and
+ // then resume reservation for more methods. This is useful when there is a gap in
+ // the .text segment, for example when going to the next oat file for multi-image.
virtual uint32_t ReserveSpaceEnd(uint32_t offset) = 0;
- // Write relative call thunks if needed, return adjusted offset.
+ // Write relative call thunks if needed, return adjusted offset. Returns 0 on write failure.
virtual uint32_t WriteThunks(OutputStream* out, uint32_t offset) = 0;
// Patch method code. The input displacement is relative to the patched location,
// the patcher may need to adjust it if the correct base is different.
- virtual void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) = 0;
+ virtual void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) = 0;
// Patch a reference to a dex cache location.
- virtual void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) = 0;
+ virtual void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) = 0;
protected:
RelativePatcher()
diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h
index bf8e786f64..704135a7b5 100644
--- a/compiler/linker/relative_patcher_test.h
+++ b/compiler/linker/relative_patcher_test.h
@@ -44,10 +44,22 @@ class RelativePatcherTest : public testing::Test {
: compiler_options_(),
verification_results_(&compiler_options_),
inliner_map_(),
- driver_(&compiler_options_, &verification_results_, &inliner_map_,
- Compiler::kQuick, instruction_set, nullptr,
- false, nullptr, nullptr, nullptr, 1u,
- false, false, nullptr, -1, nullptr, nullptr),
+ driver_(&compiler_options_,
+ &verification_results_,
+ &inliner_map_,
+ Compiler::kQuick,
+ instruction_set,
+ /* instruction_set_features*/ nullptr,
+ /* boot_image */ false,
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 1u,
+ /* dump_stats */ false,
+ /* dump_passes */ false,
+ /* timer */ nullptr,
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr),
error_msg_(),
instruction_set_(instruction_set),
features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)),
@@ -138,8 +150,10 @@ class RelativePatcherTest : public testing::Test {
offset + patch.LiteralOffset(), target_offset);
} else if (patch.Type() == kLinkerPatchDexCacheArray) {
uint32_t target_offset = dex_cache_arrays_begin_ + patch.TargetDexCacheElementOffset();
- patcher_->PatchDexCacheReference(&patched_code_, patch,
- offset + patch.LiteralOffset(), target_offset);
+ patcher_->PatchDexCacheReference(&patched_code_,
+ patch,
+ offset + patch.LiteralOffset(),
+ target_offset);
} else {
LOG(FATAL) << "Bad patch type.";
}
diff --git a/compiler/linker/x86/relative_patcher_x86.h b/compiler/linker/x86/relative_patcher_x86.h
index 0c881f00ba..ddc244c269 100644
--- a/compiler/linker/x86/relative_patcher_x86.h
+++ b/compiler/linker/x86/relative_patcher_x86.h
@@ -26,8 +26,10 @@ class X86RelativePatcher FINAL : public X86BaseRelativePatcher {
public:
X86RelativePatcher() { }
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
};
} // namespace linker
diff --git a/compiler/linker/x86/relative_patcher_x86_base.cc b/compiler/linker/x86/relative_patcher_x86_base.cc
index bc285a7849..bf3a648218 100644
--- a/compiler/linker/x86/relative_patcher_x86_base.cc
+++ b/compiler/linker/x86/relative_patcher_x86_base.cc
@@ -34,8 +34,10 @@ uint32_t X86BaseRelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED,
return offset; // No thunks added; no limit on relative call distance.
}
-void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) {
+void X86BaseRelativePatcher::PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(literal_offset + 4u, code->size());
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
diff --git a/compiler/linker/x86/relative_patcher_x86_base.h b/compiler/linker/x86/relative_patcher_x86_base.h
index 9200709398..ca83a72f48 100644
--- a/compiler/linker/x86/relative_patcher_x86_base.h
+++ b/compiler/linker/x86/relative_patcher_x86_base.h
@@ -29,8 +29,10 @@ class X86BaseRelativePatcher : public RelativePatcher {
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
- void PatchCall(std::vector<uint8_t>* code, uint32_t literal_offset,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchCall(std::vector<uint8_t>* code,
+ uint32_t literal_offset,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
protected:
X86BaseRelativePatcher() { }
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.cc b/compiler/linker/x86_64/relative_patcher_x86_64.cc
index 598f3ac4a8..e571f50d2f 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.cc
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.cc
@@ -23,7 +23,8 @@ namespace linker {
void X86_64RelativePatcher::PatchDexCacheReference(std::vector<uint8_t>* code,
const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) {
+ uint32_t patch_offset,
+ uint32_t target_offset) {
DCHECK_LE(patch.LiteralOffset() + 4u, code->size());
// Unsigned arithmetic with its well-defined overflow behavior is just fine here.
uint32_t displacement = target_offset - patch_offset;
diff --git a/compiler/linker/x86_64/relative_patcher_x86_64.h b/compiler/linker/x86_64/relative_patcher_x86_64.h
index af687b4a2f..feecb3a2ad 100644
--- a/compiler/linker/x86_64/relative_patcher_x86_64.h
+++ b/compiler/linker/x86_64/relative_patcher_x86_64.h
@@ -26,8 +26,10 @@ class X86_64RelativePatcher FINAL : public X86BaseRelativePatcher {
public:
X86_64RelativePatcher() { }
- void PatchDexCacheReference(std::vector<uint8_t>* code, const LinkerPatch& patch,
- uint32_t patch_offset, uint32_t target_offset) OVERRIDE;
+ void PatchDexCacheReference(std::vector<uint8_t>* code,
+ const LinkerPatch& patch,
+ uint32_t patch_offset,
+ uint32_t target_offset) OVERRIDE;
};
} // namespace linker
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 894d29ee99..14fd1054c3 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -31,6 +31,7 @@
#include "elf_writer.h"
#include "elf_writer_quick.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "linker/multi_oat_relative_patcher.h"
#include "linker/vector_output_stream.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
@@ -111,17 +112,16 @@ class OatTest : public CommonCompilerTest {
compiler_kind,
insn_set,
insn_features_.get(),
- false,
- nullptr,
- nullptr,
- nullptr,
- 2,
- true,
- true,
+ /* boot_image */ false,
+ /* image_classes */ nullptr,
+ /* compiled_classes */ nullptr,
+ /* compiled_methods */ nullptr,
+ /* thread_count */ 2,
+ /* dump_stats */ true,
+ /* dump_passes */ true,
timer_.get(),
- -1,
- nullptr,
- nullptr));
+ /* swap_fd */ -1,
+ /* profile_compilation_info */ nullptr));
}
bool WriteElf(File* file,
@@ -200,7 +200,13 @@ class OatTest : public CommonCompilerTest {
ScopedObjectAccess soa(Thread::Current());
class_linker->RegisterDexFile(*dex_file, runtime->GetLinearAlloc());
}
- oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files);
+ linker::MultiOatRelativePatcher patcher(compiler_driver_->GetInstructionSet(),
+ instruction_set_features_.get());
+ oat_writer.PrepareLayout(compiler_driver_.get(), nullptr, dex_files, &patcher);
+ size_t rodata_size = oat_writer.GetOatHeader().GetExecutableOffset();
+ size_t text_size = oat_writer.GetSize() - rodata_size;
+ elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer.GetBssSize());
+
if (!oat_writer.WriteRodata(rodata)) {
return false;
}
@@ -216,7 +222,6 @@ class OatTest : public CommonCompilerTest {
return false;
}
- elf_writer->SetBssSize(oat_writer.GetBssSize());
elf_writer->WriteDynamicSection();
elf_writer->WriteDebugInfo(oat_writer.GetMethodDebugInfo());
elf_writer->WritePatchLocations(oat_writer.GetAbsolutePatchLocations());
@@ -415,7 +420,9 @@ TEST_F(OatTest, WriteRead) {
size_t visited_virtuals = 0;
// TODO We should also check copied methods in this test.
for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) {
- EXPECT_FALSE(m.IsMiranda());
+ if (!klass->IsInterface()) {
+ EXPECT_FALSE(m.IsCopied());
+ }
CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
++method_index;
++visited_virtuals;
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 47dcfd56f8..c60b02a227 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -38,8 +38,8 @@
#include "gc/space/space.h"
#include "handle_scope-inl.h"
#include "image_writer.h"
+#include "linker/multi_oat_relative_patcher.h"
#include "linker/output_stream.h"
-#include "linker/relative_patcher.h"
#include "mirror/array.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
@@ -292,7 +292,8 @@ OatWriter::OatWriter(bool compiling_boot_image, TimingLogger* timings)
size_oat_class_status_(0),
size_oat_class_method_bitmaps_(0),
size_oat_class_method_offsets_(0),
- method_offset_map_() {
+ relative_patcher_(nullptr),
+ absolute_patch_locations_() {
}
bool OatWriter::AddDexFileSource(const char* filename,
@@ -438,21 +439,21 @@ bool OatWriter::WriteAndOpenDexFiles(
void OatWriter::PrepareLayout(const CompilerDriver* compiler,
ImageWriter* image_writer,
- const std::vector<const DexFile*>& dex_files) {
+ const std::vector<const DexFile*>& dex_files,
+ linker::MultiOatRelativePatcher* relative_patcher) {
CHECK(write_state_ == WriteState::kPrepareLayout);
- dex_files_ = &dex_files;
-
compiler_driver_ = compiler;
image_writer_ = image_writer;
+ dex_files_ = &dex_files;
+ relative_patcher_ = relative_patcher;
+ SetMultiOatRelativePatcherAdjustment();
+
if (compiling_boot_image_) {
CHECK(image_writer_ != nullptr);
}
InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
CHECK_EQ(instruction_set, oat_header_->GetInstructionSet());
- const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures();
- relative_patcher_ = linker::RelativePatcher::Create(instruction_set, features,
- &method_offset_map_);
uint32_t offset = size_;
{
@@ -727,13 +728,11 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
// Deduplicate code arrays if we are not producing debuggable code.
bool deduped = false;
MethodReference method_ref(dex_file_, it.GetMemberIndex());
- auto method_lb = writer_->method_offset_map_.map.lower_bound(method_ref);
if (debuggable_) {
- if (method_lb != writer_->method_offset_map_.map.end() &&
- !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) {
+ quick_code_offset = writer_->relative_patcher_->GetOffset(method_ref);
+ if (quick_code_offset != 0u) {
// Duplicate methods, we want the same code for both of them so that the oat writer puts
// the same code in both ArtMethods so that we do not get different oat code at runtime.
- quick_code_offset = method_lb->second;
deduped = true;
} else {
quick_code_offset = NewQuickCodeOffset(compiled_method, it, thumb_offset);
@@ -750,14 +749,14 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
}
if (code_size != 0) {
- if (method_lb != writer_->method_offset_map_.map.end() &&
- !writer_->method_offset_map_.map.key_comp()(method_ref, method_lb->first)) {
+ if (writer_->relative_patcher_->GetOffset(method_ref) != 0u) {
// TODO: Should this be a hard failure?
LOG(WARNING) << "Multiple definitions of "
<< PrettyMethod(method_ref.dex_method_index, *method_ref.dex_file)
- << " offsets " << method_lb->second << " " << quick_code_offset;
+ << " offsets " << writer_->relative_patcher_->GetOffset(method_ref)
+ << " " << quick_code_offset;
} else {
- writer_->method_offset_map_.map.PutBefore(method_lb, method_ref, quick_code_offset);
+ writer_->relative_patcher_->SetOffset(method_ref, quick_code_offset);
}
}
@@ -1106,27 +1105,29 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
patched_code_.assign(quick_code.begin(), quick_code.end());
quick_code = ArrayRef<const uint8_t>(patched_code_);
for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ uint32_t literal_offset = patch.LiteralOffset();
if (patch.Type() == kLinkerPatchCallRelative) {
// NOTE: Relative calls across oat files are not supported.
uint32_t target_offset = GetTargetOffset(patch);
- uint32_t literal_offset = patch.LiteralOffset();
- writer_->relative_patcher_->PatchCall(&patched_code_, literal_offset,
- offset_ + literal_offset, target_offset);
+ writer_->relative_patcher_->PatchCall(&patched_code_,
+ literal_offset,
+ offset_ + literal_offset,
+ target_offset);
} else if (patch.Type() == kLinkerPatchDexCacheArray) {
uint32_t target_offset = GetDexCacheOffset(patch);
- uint32_t literal_offset = patch.LiteralOffset();
- writer_->relative_patcher_->PatchDexCacheReference(&patched_code_, patch,
+ writer_->relative_patcher_->PatchDexCacheReference(&patched_code_,
+ patch,
offset_ + literal_offset,
target_offset);
} else if (patch.Type() == kLinkerPatchCall) {
uint32_t target_offset = GetTargetOffset(patch);
- PatchCodeAddress(&patched_code_, patch.LiteralOffset(), target_offset);
+ PatchCodeAddress(&patched_code_, literal_offset, target_offset);
} else if (patch.Type() == kLinkerPatchMethod) {
ArtMethod* method = GetTargetMethod(patch);
- PatchMethodAddress(&patched_code_, patch.LiteralOffset(), method);
+ PatchMethodAddress(&patched_code_, literal_offset, method);
} else if (patch.Type() == kLinkerPatchType) {
mirror::Class* type = GetTargetType(patch);
- PatchObjectAddress(&patched_code_, patch.LiteralOffset(), type);
+ PatchObjectAddress(&patched_code_, literal_offset, type);
}
}
}
@@ -1172,16 +1173,16 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
uint32_t GetTargetOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
- auto target_it = writer_->method_offset_map_.map.find(patch.TargetMethod());
- uint32_t target_offset =
- (target_it != writer_->method_offset_map_.map.end()) ? target_it->second : 0u;
- // If there's no compiled code, point to the correct trampoline.
+ uint32_t target_offset = writer_->relative_patcher_->GetOffset(patch.TargetMethod());
+ // If there's no new compiled code, either we're compiling an app and the target method
+ // is in the boot image, or we need to point to the correct trampoline.
if (UNLIKELY(target_offset == 0)) {
ArtMethod* target = GetTargetMethod(patch);
DCHECK(target != nullptr);
size_t size = GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet());
const void* oat_code_offset = target->GetEntryPointFromQuickCompiledCodePtrSize(size);
if (oat_code_offset != 0) {
+ DCHECK(!writer_->HasBootImage());
DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(oat_code_offset));
DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(oat_code_offset));
DCHECK(!Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(oat_code_offset));
@@ -1206,11 +1207,10 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
uint32_t GetDexCacheOffset(const LinkerPatch& patch) SHARED_REQUIRES(Locks::mutator_lock_) {
if (writer_->HasBootImage()) {
- auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>(
- patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
- const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_);
- const uint8_t* oat_data =
- writer_->image_writer_->GetOatFileBegin(oat_filename) + file_offset_;
+ uintptr_t element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<uintptr_t>(
+ patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset());
+ size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_);
+ uintptr_t oat_data = writer_->image_writer_->GetOatDataBegin(oat_index);
return element - oat_data;
} else {
size_t start = writer_->dex_cache_arrays_offsets_.Get(patch.TargetDexCacheDexFile());
@@ -1270,9 +1270,13 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
SHARED_REQUIRES(Locks::mutator_lock_) {
uint32_t address = target_offset;
if (writer_->HasBootImage()) {
- const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_);
- address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin(oat_filename) +
- writer_->oat_data_offset_ + target_offset);
+ size_t oat_index = writer_->image_writer_->GetOatIndexForDexCache(dex_cache_);
+ // TODO: Clean up offset types.
+ // The target_offset must be treated as signed for cross-oat patching.
+ const void* target = reinterpret_cast<const void*>(
+ writer_->image_writer_->GetOatDataBegin(oat_index) +
+ static_cast<int32_t>(target_offset));
+ address = PointerToLowMemUInt32(target);
}
DCHECK_LE(offset + 4, code->size());
uint8_t* data = &(*code)[offset];
@@ -1540,6 +1544,8 @@ bool OatWriter::WriteRodata(OutputStream* out) {
bool OatWriter::WriteCode(OutputStream* out) {
CHECK(write_state_ == WriteState::kWriteText);
+ SetMultiOatRelativePatcherAdjustment();
+
const size_t file_offset = oat_data_offset_;
size_t relative_offset = oat_header_->GetExecutableOffset();
DCHECK_OFFSET();
@@ -1781,7 +1787,7 @@ size_t OatWriter::WriteCodeDexFiles(OutputStream* out,
return relative_offset;
}
-bool OatWriter::GetOatDataOffset(OutputStream* out) {
+bool OatWriter::RecordOatDataOffset(OutputStream* out) {
// Get the elf file offset of the oat file.
const off_t raw_file_offset = out->Seek(0, kSeekCurrent);
if (raw_file_offset == static_cast<off_t>(-1)) {
@@ -1833,7 +1839,7 @@ bool OatWriter::WriteDexFiles(OutputStream* rodata, File* file) {
TimingLogger::ScopedTiming split("WriteDexFiles", timings_);
// Get the elf file offset of the oat file.
- if (!GetOatDataOffset(rodata)) {
+ if (!RecordOatDataOffset(rodata)) {
return false;
}
@@ -2261,12 +2267,15 @@ bool OatWriter::WriteData(OutputStream* out, const void* data, size_t size) {
return out->WriteFully(data, size);
}
-std::pair<bool, uint32_t> OatWriter::MethodOffsetMap::FindMethodOffset(MethodReference ref) {
- auto it = map.find(ref);
- if (it == map.end()) {
- return std::pair<bool, uint32_t>(false, 0u);
- } else {
- return std::pair<bool, uint32_t>(true, it->second);
+void OatWriter::SetMultiOatRelativePatcherAdjustment() {
+ DCHECK(dex_files_ != nullptr);
+ DCHECK(relative_patcher_ != nullptr);
+ DCHECK_NE(oat_data_offset_, 0u);
+ if (image_writer_ != nullptr && !dex_files_->empty()) {
+ // The oat data begin may not be initialized yet but the oat file offset is ready.
+ size_t oat_index = image_writer_->GetOatIndexForDexFile(dex_files_->front());
+ size_t elf_file_offset = image_writer_->GetOatFileOffset(oat_index);
+ relative_patcher_->StartOatFile(elf_file_offset + oat_data_offset_);
}
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 5a55fc6c95..74aab4efd0 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -47,6 +47,10 @@ namespace debug {
struct MethodDebugInfo;
} // namespace debug
+namespace linker {
+class MultiOatRelativePatcher;
+} // namespace linker
+
// OatHeader variable length with count of D OatDexFiles
//
// OatDexFile[0] one variable sized OatDexFile with offsets to Dex and OatClasses
@@ -153,7 +157,8 @@ class OatWriter {
// Prepare layout of remaining data.
void PrepareLayout(const CompilerDriver* compiler,
ImageWriter* image_writer,
- const std::vector<const DexFile*>& dex_files);
+ const std::vector<const DexFile*>& dex_files,
+ linker::MultiOatRelativePatcher* relative_patcher);
// Write the rest of .rodata section (ClassOffsets[], OatClass[], maps).
bool WriteRodata(OutputStream* out);
// Write the code to the .text section.
@@ -187,6 +192,10 @@ class OatWriter {
return bss_size_;
}
+ size_t GetOatDataOffset() const {
+ return oat_data_offset_;
+ }
+
ArrayRef<const uintptr_t> GetAbsolutePatchLocations() const {
return ArrayRef<const uintptr_t>(absolute_patch_locations_);
}
@@ -249,7 +258,7 @@ class OatWriter {
size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
- bool GetOatDataOffset(OutputStream* out);
+ bool RecordOatDataOffset(OutputStream* out);
bool ReadDexFileHeader(File* file, OatDexFile* oat_dex_file);
bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location);
bool WriteDexFiles(OutputStream* rodata, File* file);
@@ -268,6 +277,7 @@ class OatWriter {
const std::vector<std::unique_ptr<const DexFile>>& opened_dex_files);
bool WriteCodeAlignment(OutputStream* out, uint32_t aligned_code_delta);
bool WriteData(OutputStream* out, const void* data, size_t size);
+ void SetMultiOatRelativePatcherAdjustment();
enum class WriteState {
kAddingDexFileSources,
@@ -358,20 +368,12 @@ class OatWriter {
uint32_t size_oat_class_method_bitmaps_;
uint32_t size_oat_class_method_offsets_;
- std::unique_ptr<linker::RelativePatcher> relative_patcher_;
+ // The helper for processing relative patches is external so that we can patch across oat files.
+ linker::MultiOatRelativePatcher* relative_patcher_;
// The locations of absolute patches relative to the start of the executable section.
dchecked_vector<uintptr_t> absolute_patch_locations_;
- // Map method reference to assigned offset.
- // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
- class MethodOffsetMap FINAL : public linker::RelativePatcherTargetProvider {
- public:
- std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE;
- SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map;
- };
- MethodOffsetMap method_offset_map_;
-
DISALLOW_COPY_AND_ASSIGN(OatWriter);
};
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index ba1b1683d7..a7a1c0f2c4 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -67,20 +67,28 @@ class ValueBound : public ValueObject {
static bool IsAddOrSubAConstant(HInstruction* instruction,
/* out */ HInstruction** left_instruction,
/* out */ int32_t* right_constant) {
- if (instruction->IsAdd() || instruction->IsSub()) {
+ HInstruction* left_so_far = nullptr;
+ int32_t right_so_far = 0;
+ while (instruction->IsAdd() || instruction->IsSub()) {
HBinaryOperation* bin_op = instruction->AsBinaryOperation();
HInstruction* left = bin_op->GetLeft();
HInstruction* right = bin_op->GetRight();
if (right->IsIntConstant()) {
- *left_instruction = left;
- int32_t c = right->AsIntConstant()->GetValue();
- *right_constant = instruction->IsAdd() ? c : -c;
- return true;
+ int32_t v = right->AsIntConstant()->GetValue();
+ int32_t c = instruction->IsAdd() ? v : -v;
+ if (!WouldAddOverflowOrUnderflow(right_so_far, c)) {
+ instruction = left;
+ left_so_far = left;
+ right_so_far += c;
+ continue;
+ }
}
+ break;
}
- *left_instruction = nullptr;
- *right_constant = 0;
- return false;
+ // Return result: either false and "null+0" or true and "instr+constant".
+ *left_instruction = left_so_far;
+ *right_constant = right_so_far;
+ return left_so_far != nullptr;
}
// Expresses any instruction as a value bound.
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 05e1356ed8..35ec7d41ff 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -368,7 +368,6 @@ GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item
if (native_debuggable) {
const uint32_t num_instructions = code_item.insns_size_in_code_units_;
native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false);
- native_debug_info_locations->ClearAllBits();
FindNativeDebugInfoLocations(code_item, native_debug_info_locations);
}
@@ -443,23 +442,15 @@ void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_i
}
};
dex_file_->DecodeDebugPositionInfo(&code_item, Callback::Position, locations);
- // Add native debug info at the start of every basic block.
- for (uint32_t pc = 0; pc < code_item.insns_size_in_code_units_; pc++) {
- if (FindBlockStartingAt(pc) != nullptr) {
- locations->SetBit(pc);
- }
- }
// Instruction-specific tweaks.
const Instruction* const begin = Instruction::At(code_item.insns_);
const Instruction* const end = begin->RelativeAt(code_item.insns_size_in_code_units_);
for (const Instruction* inst = begin; inst < end; inst = inst->Next()) {
switch (inst->Opcode()) {
- case Instruction::MOVE_EXCEPTION:
- case Instruction::MOVE_RESULT:
- case Instruction::MOVE_RESULT_WIDE:
- case Instruction::MOVE_RESULT_OBJECT: {
- // The compiler checks that there are no instructions before those.
- // So generate HNativeDebugInfo after them instead.
+ case Instruction::MOVE_EXCEPTION: {
+ // Stop in native debugger after the exception has been moved.
+ // The compiler also expects the move at the start of basic block so
+ // we do not want to interfere by inserting native-debug-info before it.
locations->ClearBit(inst->GetDexPc(code_item.insns_));
const Instruction* next = inst->Next();
if (next < end) {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index c2c8ccfc56..967d156cf6 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -195,6 +195,8 @@ void CodeGenerator::GenerateSlowPaths() {
if (disasm_info_ != nullptr) {
code_start = GetAssembler()->CodeSize();
}
+ // Record the dex pc at start of slow path (required for java line number mapping).
+ MaybeRecordNativeDebugInfo(nullptr /* instruction */, slow_path->GetDexPc());
slow_path->EmitNativeCode(this);
if (disasm_info_ != nullptr) {
disasm_info_->AddSlowPathInterval(slow_path, code_start, GetAssembler()->CodeSize());
@@ -226,6 +228,10 @@ void CodeGenerator::Compile(CodeAllocator* allocator) {
// errors where we reference that label.
if (block->IsSingleJump()) continue;
Bind(block);
+ // This ensures that we have correct native line mapping for all native instructions.
+ // It is necessary to make stepping over a statement work. Otherwise, any initial
+ // instructions (e.g. moves) would be assumed to be the start of next statement.
+ MaybeRecordNativeDebugInfo(nullptr /* instruction */, block->GetDexPc());
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
DisassemblyScope disassembly_scope(current, *this);
@@ -733,7 +739,8 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction,
uint32_t native_pc = GetAssembler()->CodeSize();
if (instruction == nullptr) {
- // For stack overflow checks.
+ // For stack overflow checks and native-debug-info entries without dex register
+ // mapping (i.e. start of basic block or start of slow path).
stack_map_stream_.BeginStackMapEntry(outer_dex_pc, native_pc, 0, 0, 0, 0);
stack_map_stream_.EndStackMapEntry();
return;
@@ -808,6 +815,16 @@ bool CodeGenerator::HasStackMapAtCurrentPc() {
return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc;
}
+void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc) {
+ if (GetCompilerOptions().GetNativeDebuggable() && dex_pc != kNoDexPc) {
+ if (HasStackMapAtCurrentPc()) {
+ // Ensure that we do not collide with the stack map of the previous instruction.
+ GenerateNop();
+ }
+ RecordPcInfo(instruction, dex_pc);
+ }
+}
+
void CodeGenerator::RecordCatchBlockInfo() {
ArenaAllocator* arena = graph_->GetArena();
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 49c193e7bf..9297fc956f 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -69,7 +69,7 @@ class CodeAllocator {
class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> {
public:
- SlowPathCode() {
+ explicit SlowPathCode(HInstruction* instruction) : instruction_(instruction) {
for (size_t i = 0; i < kMaximumNumberOfExpectedRegisters; ++i) {
saved_core_stack_offsets_[i] = kRegisterNotSaved;
saved_fpu_stack_offsets_[i] = kRegisterNotSaved;
@@ -106,9 +106,15 @@ class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> {
Label* GetEntryLabel() { return &entry_label_; }
Label* GetExitLabel() { return &exit_label_; }
+ uint32_t GetDexPc() const {
+ return instruction_ != nullptr ? instruction_->GetDexPc() : kNoDexPc;
+ }
+
protected:
static constexpr size_t kMaximumNumberOfExpectedRegisters = 32;
static constexpr uint32_t kRegisterNotSaved = -1;
+ // The instruction where this slow path is happening.
+ HInstruction* instruction_;
uint32_t saved_core_stack_offsets_[kMaximumNumberOfExpectedRegisters];
uint32_t saved_fpu_stack_offsets_[kMaximumNumberOfExpectedRegisters];
@@ -267,6 +273,8 @@ class CodeGenerator {
void RecordPcInfo(HInstruction* instruction, uint32_t dex_pc, SlowPathCode* slow_path = nullptr);
// Check whether we have already recorded mapping at this PC.
bool HasStackMapAtCurrentPc();
+ // Record extra stack maps if we support native debugging.
+ void MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc);
bool CanMoveNullCheckToUser(HNullCheck* null_check);
void MaybeRecordImplicitNullCheck(HInstruction* instruction);
@@ -440,6 +448,8 @@ class CodeGenerator {
// Copy the result of a call into the given target.
virtual void MoveFromReturnRegister(Location trg, Primitive::Type type) = 0;
+ virtual void GenerateNop() = 0;
+
protected:
// Method patch info used for recording locations of required linker patches and
// target methods. The target method can be used for various purposes, whether for
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 87f52c6f21..cdbb9c31aa 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -64,7 +64,7 @@ static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
class NullCheckSlowPathARM : public SlowPathCode {
public:
- explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
+ explicit NullCheckSlowPathARM(HNullCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -83,13 +83,12 @@ class NullCheckSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM"; }
private:
- HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
};
class DivZeroCheckSlowPathARM : public SlowPathCode {
public:
- explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : instruction_(instruction) {}
+ explicit DivZeroCheckSlowPathARM(HDivZeroCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -108,14 +107,13 @@ class DivZeroCheckSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM"; }
private:
- HDivZeroCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM);
};
class SuspendCheckSlowPathARM : public SlowPathCode {
public:
SuspendCheckSlowPathARM(HSuspendCheck* instruction, HBasicBlock* successor)
- : instruction_(instruction), successor_(successor) {}
+ : SlowPathCode(instruction), successor_(successor) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -144,7 +142,6 @@ class SuspendCheckSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM"; }
private:
- HSuspendCheck* const instruction_;
// If not null, the block to branch to after the suspend check.
HBasicBlock* const successor_;
@@ -157,7 +154,7 @@ class SuspendCheckSlowPathARM : public SlowPathCode {
class BoundsCheckSlowPathARM : public SlowPathCode {
public:
explicit BoundsCheckSlowPathARM(HBoundsCheck* instruction)
- : instruction_(instruction) {}
+ : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -188,8 +185,6 @@ class BoundsCheckSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM"; }
private:
- HBoundsCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
};
@@ -199,7 +194,7 @@ class LoadClassSlowPathARM : public SlowPathCode {
HInstruction* at,
uint32_t dex_pc,
bool do_clinit)
- : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -253,7 +248,7 @@ class LoadClassSlowPathARM : public SlowPathCode {
class LoadStringSlowPathARM : public SlowPathCode {
public:
- explicit LoadStringSlowPathARM(HLoadString* instruction) : instruction_(instruction) {}
+ explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -264,7 +259,8 @@ class LoadStringSlowPathARM : public SlowPathCode {
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
- __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex());
+ const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
+ __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index);
arm_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this);
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
@@ -277,15 +273,13 @@ class LoadStringSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM"; }
private:
- HLoadString* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM);
};
class TypeCheckSlowPathARM : public SlowPathCode {
public:
TypeCheckSlowPathARM(HInstruction* instruction, bool is_fatal)
- : instruction_(instruction), is_fatal_(is_fatal) {}
+ : SlowPathCode(instruction), is_fatal_(is_fatal) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -340,7 +334,6 @@ class TypeCheckSlowPathARM : public SlowPathCode {
bool IsFatal() const OVERRIDE { return is_fatal_; }
private:
- HInstruction* const instruction_;
const bool is_fatal_;
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM);
@@ -349,7 +342,7 @@ class TypeCheckSlowPathARM : public SlowPathCode {
class DeoptimizationSlowPathARM : public SlowPathCode {
public:
explicit DeoptimizationSlowPathARM(HDeoptimize* instruction)
- : instruction_(instruction) {}
+ : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
@@ -365,13 +358,12 @@ class DeoptimizationSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM"; }
private:
- HDeoptimize* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM);
};
class ArraySetSlowPathARM : public SlowPathCode {
public:
- explicit ArraySetSlowPathARM(HInstruction* instruction) : instruction_(instruction) {}
+ explicit ArraySetSlowPathARM(HInstruction* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -410,8 +402,6 @@ class ArraySetSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM"; }
private:
- HInstruction* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM);
};
@@ -419,7 +409,7 @@ class ArraySetSlowPathARM : public SlowPathCode {
class ReadBarrierMarkSlowPathARM : public SlowPathCode {
public:
ReadBarrierMarkSlowPathARM(HInstruction* instruction, Location out, Location obj)
- : instruction_(instruction), out_(out), obj_(obj) {
+ : SlowPathCode(instruction), out_(out), obj_(obj) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -458,7 +448,6 @@ class ReadBarrierMarkSlowPathARM : public SlowPathCode {
}
private:
- HInstruction* const instruction_;
const Location out_;
const Location obj_;
@@ -474,7 +463,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode {
Location obj,
uint32_t offset,
Location index)
- : instruction_(instruction),
+ : SlowPathCode(instruction),
out_(out),
ref_(ref),
obj_(obj),
@@ -629,7 +618,6 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode {
UNREACHABLE();
}
- HInstruction* const instruction_;
const Location out_;
const Location ref_;
const Location obj_;
@@ -646,7 +634,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode {
class ReadBarrierForRootSlowPathARM : public SlowPathCode {
public:
ReadBarrierForRootSlowPathARM(HInstruction* instruction, Location out, Location root)
- : instruction_(instruction), out_(out), root_(root) {
+ : SlowPathCode(instruction), out_(out), root_(root) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -679,7 +667,6 @@ class ReadBarrierForRootSlowPathARM : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM"; }
private:
- HInstruction* const instruction_;
const Location out_;
const Location root_;
@@ -1557,11 +1544,11 @@ void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
}
void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- if (codegen_->HasStackMapAtCurrentPc()) {
- // Ensure that we do not collide with the stack map of the previous instruction.
- __ nop();
- }
- codegen_->RecordPcInfo(info, info->GetDexPc());
+ codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc());
+}
+
+void CodeGeneratorARM::GenerateNop() {
+ __ nop();
}
void LocationsBuilderARM::HandleCondition(HCondition* cond) {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index cfd7a3bc14..2e4dc1e014 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -510,6 +510,8 @@ class CodeGeneratorARM : public CodeGenerator {
// artReadBarrierForRootSlow.
void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root);
+ void GenerateNop();
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 435ae5e954..814f8b4d51 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -219,7 +219,7 @@ void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSum
class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : instruction_(instruction) {}
+ explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : SlowPathCodeARM64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -246,14 +246,12 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; }
private:
- HBoundsCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
};
class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : instruction_(instruction) {}
+ explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : SlowPathCodeARM64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
@@ -272,7 +270,6 @@ class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; }
private:
- HDivZeroCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64);
};
@@ -282,7 +279,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
HInstruction* at,
uint32_t dex_pc,
bool do_clinit)
- : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ : SlowPathCodeARM64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -337,7 +334,7 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit LoadStringSlowPathARM64(HLoadString* instruction) : instruction_(instruction) {}
+ explicit LoadStringSlowPathARM64(HLoadString* instruction) : SlowPathCodeARM64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -348,7 +345,8 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
- __ Mov(calling_convention.GetRegisterAt(0).W(), instruction_->GetStringIndex());
+ const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
+ __ Mov(calling_convention.GetRegisterAt(0).W(), string_index);
arm64_codegen->InvokeRuntime(
QUICK_ENTRY_POINT(pResolveString), instruction_, instruction_->GetDexPc(), this);
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
@@ -362,14 +360,12 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; }
private:
- HLoadString* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64);
};
class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit NullCheckSlowPathARM64(HNullCheck* instr) : instruction_(instr) {}
+ explicit NullCheckSlowPathARM64(HNullCheck* instr) : SlowPathCodeARM64(instr) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
@@ -388,15 +384,13 @@ class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; }
private:
- HNullCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64);
};
class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
SuspendCheckSlowPathARM64(HSuspendCheck* instruction, HBasicBlock* successor)
- : instruction_(instruction), successor_(successor) {}
+ : SlowPathCodeARM64(instruction), successor_(successor) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
@@ -425,7 +419,6 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; }
private:
- HSuspendCheck* const instruction_;
// If not null, the block to branch to after the suspend check.
HBasicBlock* const successor_;
@@ -438,7 +431,7 @@ class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
public:
TypeCheckSlowPathARM64(HInstruction* instruction, bool is_fatal)
- : instruction_(instruction), is_fatal_(is_fatal) {}
+ : SlowPathCodeARM64(instruction), is_fatal_(is_fatal) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -487,7 +480,6 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
bool IsFatal() const { return is_fatal_; }
private:
- HInstruction* const instruction_;
const bool is_fatal_;
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64);
@@ -496,7 +488,7 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 {
public:
explicit DeoptimizationSlowPathARM64(HDeoptimize* instruction)
- : instruction_(instruction) {}
+ : SlowPathCodeARM64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
@@ -512,13 +504,12 @@ class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; }
private:
- HDeoptimize* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64);
};
class ArraySetSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit ArraySetSlowPathARM64(HInstruction* instruction) : instruction_(instruction) {}
+ explicit ArraySetSlowPathARM64(HInstruction* instruction) : SlowPathCodeARM64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -557,8 +548,6 @@ class ArraySetSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; }
private:
- HInstruction* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64);
};
@@ -588,7 +577,7 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 {
public:
ReadBarrierMarkSlowPathARM64(HInstruction* instruction, Location out, Location obj)
- : instruction_(instruction), out_(out), obj_(obj) {
+ : SlowPathCodeARM64(instruction), out_(out), obj_(obj) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -627,7 +616,6 @@ class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 {
}
private:
- HInstruction* const instruction_;
const Location out_;
const Location obj_;
@@ -643,7 +631,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
Location obj,
uint32_t offset,
Location index)
- : instruction_(instruction),
+ : SlowPathCodeARM64(instruction),
out_(out),
ref_(ref),
obj_(obj),
@@ -804,7 +792,6 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
UNREACHABLE();
}
- HInstruction* const instruction_;
const Location out_;
const Location ref_;
const Location obj_;
@@ -821,7 +808,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 {
public:
ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root)
- : instruction_(instruction), out_(out), root_(root) {
+ : SlowPathCodeARM64(instruction), out_(out), root_(root) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -865,7 +852,6 @@ class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 {
const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM64"; }
private:
- HInstruction* const instruction_;
const Location out_;
const Location root_;
@@ -3057,11 +3043,11 @@ void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
}
void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- if (codegen_->HasStackMapAtCurrentPc()) {
- // Ensure that we do not collide with the stack map of the previous instruction.
- __ Nop();
- }
- codegen_->RecordPcInfo(info, info->GetDexPc());
+ codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc());
+}
+
+void CodeGeneratorARM64::GenerateNop() {
+ __ Nop();
}
void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 360488eb4a..3527261835 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -66,7 +66,8 @@ Location ARM64ReturnLocation(Primitive::Type return_type);
class SlowPathCodeARM64 : public SlowPathCode {
public:
- SlowPathCodeARM64() : entry_label_(), exit_label_() {}
+ explicit SlowPathCodeARM64(HInstruction* instruction)
+ : SlowPathCode(instruction), entry_label_(), exit_label_() {}
vixl::Label* GetEntryLabel() { return &entry_label_; }
vixl::Label* GetExitLabel() { return &exit_label_; }
@@ -532,6 +533,8 @@ class CodeGeneratorARM64 : public CodeGenerator {
// artReadBarrierForRootSlow.
void GenerateReadBarrierForRootSlow(HInstruction* instruction, Location out, Location root);
+ void GenerateNop();
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index c500ea4408..8d3d94b79d 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -39,9 +39,6 @@ namespace mips {
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr Register kMethodRegisterArgument = A0;
-// We need extra temporary/scratch registers (in addition to AT) in some cases.
-static constexpr FRegister FTMP = F8;
-
Location MipsReturnLocation(Primitive::Type return_type) {
switch (return_type) {
case Primitive::kPrimBoolean:
@@ -149,7 +146,7 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type)
class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : instruction_(instruction) {}
+ explicit BoundsCheckSlowPathMIPS(HBoundsCheck* instruction) : SlowPathCodeMIPS(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -181,14 +178,12 @@ class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS"; }
private:
- HBoundsCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS);
};
class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : instruction_(instruction) {}
+ explicit DivZeroCheckSlowPathMIPS(HDivZeroCheck* instruction) : SlowPathCodeMIPS(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
@@ -210,7 +205,6 @@ class DivZeroCheckSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS"; }
private:
- HDivZeroCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS);
};
@@ -220,7 +214,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
HInstruction* at,
uint32_t dex_pc,
bool do_clinit)
- : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ : SlowPathCodeMIPS(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -279,7 +273,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit LoadStringSlowPathMIPS(HLoadString* instruction) : instruction_(instruction) {}
+ explicit LoadStringSlowPathMIPS(HLoadString* instruction) : SlowPathCodeMIPS(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -290,7 +284,8 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
- __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex());
+ const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
+ __ LoadConst32(calling_convention.GetRegisterAt(0), string_index);
mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString),
instruction_,
instruction_->GetDexPc(),
@@ -309,14 +304,12 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; }
private:
- HLoadString* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS);
};
class NullCheckSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit NullCheckSlowPathMIPS(HNullCheck* instr) : instruction_(instr) {}
+ explicit NullCheckSlowPathMIPS(HNullCheck* instr) : SlowPathCodeMIPS(instr) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
@@ -338,15 +331,13 @@ class NullCheckSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS"; }
private:
- HNullCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS);
};
class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS {
public:
SuspendCheckSlowPathMIPS(HSuspendCheck* instruction, HBasicBlock* successor)
- : instruction_(instruction), successor_(successor) {}
+ : SlowPathCodeMIPS(instruction), successor_(successor) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
@@ -374,7 +365,6 @@ class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS"; }
private:
- HSuspendCheck* const instruction_;
// If not null, the block to branch to after the suspend check.
HBasicBlock* const successor_;
@@ -386,7 +376,7 @@ class SuspendCheckSlowPathMIPS : public SlowPathCodeMIPS {
class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit TypeCheckSlowPathMIPS(HInstruction* instruction) : instruction_(instruction) {}
+ explicit TypeCheckSlowPathMIPS(HInstruction* instruction) : SlowPathCodeMIPS(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -437,15 +427,13 @@ class TypeCheckSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS"; }
private:
- HInstruction* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS);
};
class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS {
public:
explicit DeoptimizationSlowPathMIPS(HDeoptimize* instruction)
- : instruction_(instruction) {}
+ : SlowPathCodeMIPS(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
@@ -462,7 +450,6 @@ class DeoptimizationSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS"; }
private:
- HDeoptimize* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS);
};
@@ -3407,11 +3394,11 @@ void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
}
void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- if (codegen_->HasStackMapAtCurrentPc()) {
- // Ensure that we do not collide with the stack map of the previous instruction.
- __ Nop();
- }
- codegen_->RecordPcInfo(info, info->GetDexPc());
+ codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc());
+}
+
+void CodeGeneratorMIPS::GenerateNop() {
+ __ Nop();
}
void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index dd0641c7ca..605c794421 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -152,7 +152,8 @@ class ParallelMoveResolverMIPS : public ParallelMoveResolverWithSwap {
class SlowPathCodeMIPS : public SlowPathCode {
public:
- SlowPathCodeMIPS() : entry_label_(), exit_label_() {}
+ explicit SlowPathCodeMIPS(HInstruction* instruction)
+ : SlowPathCode(instruction), entry_label_(), exit_label_() {}
MipsLabel* GetEntryLabel() { return &entry_label_; }
MipsLabel* GetExitLabel() { return &exit_label_; }
@@ -360,6 +361,8 @@ class CodeGeneratorMIPS : public CodeGenerator {
UNIMPLEMENTED(FATAL) << "Not implemented on MIPS";
}
+ void GenerateNop();
+
private:
// Labels for each block that will be compiled.
MipsLabel* block_labels_;
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e3a44f1c96..c2b84b4335 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -37,9 +37,6 @@ namespace mips64 {
static constexpr int kCurrentMethodStackOffset = 0;
static constexpr GpuRegister kMethodRegisterArgument = A0;
-// We need extra temporary/scratch registers (in addition to AT) in some cases.
-static constexpr FpuRegister FTMP = F8;
-
Location Mips64ReturnLocation(Primitive::Type return_type) {
switch (return_type) {
case Primitive::kPrimBoolean:
@@ -110,7 +107,7 @@ Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type type)
class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : instruction_(instruction) {}
+ explicit BoundsCheckSlowPathMIPS64(HBoundsCheck* instruction) : SlowPathCodeMIPS64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -141,14 +138,12 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathMIPS64"; }
private:
- HBoundsCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathMIPS64);
};
class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : instruction_(instruction) {}
+ explicit DivZeroCheckSlowPathMIPS64(HDivZeroCheck* instruction) : SlowPathCodeMIPS64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
@@ -169,7 +164,6 @@ class DivZeroCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathMIPS64"; }
private:
- HDivZeroCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathMIPS64);
};
@@ -179,7 +173,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
HInstruction* at,
uint32_t dex_pc,
bool do_clinit)
- : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ : SlowPathCodeMIPS64(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -234,7 +228,7 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : instruction_(instruction) {}
+ explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : SlowPathCodeMIPS64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -245,7 +239,8 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 {
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
- __ LoadConst32(calling_convention.GetRegisterAt(0), instruction_->GetStringIndex());
+ const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
+ __ LoadConst32(calling_convention.GetRegisterAt(0), string_index);
mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString),
instruction_,
instruction_->GetDexPc(),
@@ -263,14 +258,12 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; }
private:
- HLoadString* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64);
};
class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : instruction_(instr) {}
+ explicit NullCheckSlowPathMIPS64(HNullCheck* instr) : SlowPathCodeMIPS64(instr) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
@@ -291,15 +284,13 @@ class NullCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathMIPS64"; }
private:
- HNullCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathMIPS64);
};
class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
SuspendCheckSlowPathMIPS64(HSuspendCheck* instruction, HBasicBlock* successor)
- : instruction_(instruction), successor_(successor) {}
+ : SlowPathCodeMIPS64(instruction), successor_(successor) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
@@ -326,7 +317,6 @@ class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS64"; }
private:
- HSuspendCheck* const instruction_;
// If not null, the block to branch to after the suspend check.
HBasicBlock* const successor_;
@@ -338,7 +328,7 @@ class SuspendCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit TypeCheckSlowPathMIPS64(HInstruction* instruction) : instruction_(instruction) {}
+ explicit TypeCheckSlowPathMIPS64(HInstruction* instruction) : SlowPathCodeMIPS64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -384,15 +374,13 @@ class TypeCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathMIPS64"; }
private:
- HInstruction* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathMIPS64);
};
class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
explicit DeoptimizationSlowPathMIPS64(HDeoptimize* instruction)
- : instruction_(instruction) {}
+ : SlowPathCodeMIPS64(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
@@ -408,7 +396,6 @@ class DeoptimizationSlowPathMIPS64 : public SlowPathCodeMIPS64 {
const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathMIPS64"; }
private:
- HDeoptimize* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathMIPS64);
};
@@ -2732,11 +2719,11 @@ void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
}
void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- if (codegen_->HasStackMapAtCurrentPc()) {
- // Ensure that we do not collide with the stack map of the previous instruction.
- __ Nop();
- }
- codegen_->RecordPcInfo(info, info->GetDexPc());
+ codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc());
+}
+
+void CodeGeneratorMIPS64::GenerateNop() {
+ __ Nop();
}
void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction,
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index eb7315aa7a..ba9eaff46f 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -152,7 +152,8 @@ class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap {
class SlowPathCodeMIPS64 : public SlowPathCode {
public:
- SlowPathCodeMIPS64() : entry_label_(), exit_label_() {}
+ explicit SlowPathCodeMIPS64(HInstruction* instruction)
+ : SlowPathCode(instruction), entry_label_(), exit_label_() {}
Mips64Label* GetEntryLabel() { return &entry_label_; }
Mips64Label* GetExitLabel() { return &exit_label_; }
@@ -352,6 +353,8 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
UNIMPLEMENTED(FATAL) << "Not implemented on MIPS64";
}
+ void GenerateNop();
+
private:
// Labels for each block that will be compiled.
Mips64Label* block_labels_; // Indexed by block id.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f032f51649..88e42f3faf 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -52,7 +52,7 @@ static constexpr int kFakeReturnRegister = Register(8);
class NullCheckSlowPathX86 : public SlowPathCode {
public:
- explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {}
+ explicit NullCheckSlowPathX86(HNullCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
@@ -73,13 +73,12 @@ class NullCheckSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86"; }
private:
- HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
};
class DivZeroCheckSlowPathX86 : public SlowPathCode {
public:
- explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : instruction_(instruction) {}
+ explicit DivZeroCheckSlowPathX86(HDivZeroCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
@@ -100,13 +99,13 @@ class DivZeroCheckSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86"; }
private:
- HDivZeroCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86);
};
class DivRemMinusOneSlowPathX86 : public SlowPathCode {
public:
- DivRemMinusOneSlowPathX86(Register reg, bool is_div) : reg_(reg), is_div_(is_div) {}
+ DivRemMinusOneSlowPathX86(HInstruction* instruction, Register reg, bool is_div)
+ : SlowPathCode(instruction), reg_(reg), is_div_(is_div) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
@@ -128,7 +127,7 @@ class DivRemMinusOneSlowPathX86 : public SlowPathCode {
class BoundsCheckSlowPathX86 : public SlowPathCode {
public:
- explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction) : instruction_(instruction) {}
+ explicit BoundsCheckSlowPathX86(HBoundsCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -160,15 +159,13 @@ class BoundsCheckSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86"; }
private:
- HBoundsCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86);
};
class SuspendCheckSlowPathX86 : public SlowPathCode {
public:
SuspendCheckSlowPathX86(HSuspendCheck* instruction, HBasicBlock* successor)
- : instruction_(instruction), successor_(successor) {}
+ : SlowPathCode(instruction), successor_(successor) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
@@ -199,7 +196,6 @@ class SuspendCheckSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86"; }
private:
- HSuspendCheck* const instruction_;
HBasicBlock* const successor_;
Label return_label_;
@@ -208,7 +204,7 @@ class SuspendCheckSlowPathX86 : public SlowPathCode {
class LoadStringSlowPathX86 : public SlowPathCode {
public:
- explicit LoadStringSlowPathX86(HLoadString* instruction) : instruction_(instruction) {}
+ explicit LoadStringSlowPathX86(HLoadString* instruction): SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -219,7 +215,8 @@ class LoadStringSlowPathX86 : public SlowPathCode {
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
- __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction_->GetStringIndex()));
+ const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
+ __ movl(calling_convention.GetRegisterAt(0), Immediate(string_index));
x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString),
instruction_,
instruction_->GetDexPc(),
@@ -234,8 +231,6 @@ class LoadStringSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86"; }
private:
- HLoadString* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86);
};
@@ -245,7 +240,7 @@ class LoadClassSlowPathX86 : public SlowPathCode {
HInstruction* at,
uint32_t dex_pc,
bool do_clinit)
- : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -299,7 +294,7 @@ class LoadClassSlowPathX86 : public SlowPathCode {
class TypeCheckSlowPathX86 : public SlowPathCode {
public:
TypeCheckSlowPathX86(HInstruction* instruction, bool is_fatal)
- : instruction_(instruction), is_fatal_(is_fatal) {}
+ : SlowPathCode(instruction), is_fatal_(is_fatal) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -356,7 +351,6 @@ class TypeCheckSlowPathX86 : public SlowPathCode {
bool IsFatal() const OVERRIDE { return is_fatal_; }
private:
- HInstruction* const instruction_;
const bool is_fatal_;
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86);
@@ -365,7 +359,7 @@ class TypeCheckSlowPathX86 : public SlowPathCode {
class DeoptimizationSlowPathX86 : public SlowPathCode {
public:
explicit DeoptimizationSlowPathX86(HDeoptimize* instruction)
- : instruction_(instruction) {}
+ : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
@@ -381,13 +375,12 @@ class DeoptimizationSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86"; }
private:
- HDeoptimize* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86);
};
class ArraySetSlowPathX86 : public SlowPathCode {
public:
- explicit ArraySetSlowPathX86(HInstruction* instruction) : instruction_(instruction) {}
+ explicit ArraySetSlowPathX86(HInstruction* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -426,8 +419,6 @@ class ArraySetSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86"; }
private:
- HInstruction* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86);
};
@@ -435,7 +426,7 @@ class ArraySetSlowPathX86 : public SlowPathCode {
class ReadBarrierMarkSlowPathX86 : public SlowPathCode {
public:
ReadBarrierMarkSlowPathX86(HInstruction* instruction, Location out, Location obj)
- : instruction_(instruction), out_(out), obj_(obj) {
+ : SlowPathCode(instruction), out_(out), obj_(obj) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -474,7 +465,6 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode {
}
private:
- HInstruction* const instruction_;
const Location out_;
const Location obj_;
@@ -490,7 +480,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode {
Location obj,
uint32_t offset,
Location index)
- : instruction_(instruction),
+ : SlowPathCode(instruction),
out_(out),
ref_(ref),
obj_(obj),
@@ -645,7 +635,6 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode {
UNREACHABLE();
}
- HInstruction* const instruction_;
const Location out_;
const Location ref_;
const Location obj_;
@@ -662,7 +651,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode {
class ReadBarrierForRootSlowPathX86 : public SlowPathCode {
public:
ReadBarrierForRootSlowPathX86(HInstruction* instruction, Location out, Location root)
- : instruction_(instruction), out_(out), root_(root) {
+ : SlowPathCode(instruction), out_(out), root_(root) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -695,7 +684,6 @@ class ReadBarrierForRootSlowPathX86 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathX86"; }
private:
- HInstruction* const instruction_;
const Location out_;
const Location root_;
@@ -1649,11 +1637,11 @@ void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
}
void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- if (codegen_->HasStackMapAtCurrentPc()) {
- // Ensure that we do not collide with the stack map of the previous instruction.
- __ nop();
- }
- codegen_->RecordPcInfo(info, info->GetDexPc());
+ codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc());
+}
+
+void CodeGeneratorX86::GenerateNop() {
+ __ nop();
}
void LocationsBuilderX86::VisitLocal(HLocal* local) {
@@ -3453,9 +3441,8 @@ void InstructionCodeGeneratorX86::GenerateDivRemIntegral(HBinaryOperation* instr
GenerateDivRemWithAnyConstant(instruction);
}
} else {
- SlowPathCode* slow_path =
- new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(out.AsRegister<Register>(),
- is_div);
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86(
+ instruction, out.AsRegister<Register>(), is_div);
codegen_->AddSlowPath(slow_path);
Register second_reg = second.AsRegister<Register>();
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 63e9b2fc9c..0795f3b530 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -540,6 +540,7 @@ class CodeGeneratorX86 : public CodeGenerator {
}
}
+ void GenerateNop();
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index a53a6be3de..bb24c6f59c 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -56,7 +56,7 @@ static constexpr int kC2ConditionMask = 0x400;
class NullCheckSlowPathX86_64 : public SlowPathCode {
public:
- explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : instruction_(instruction) {}
+ explicit NullCheckSlowPathX86_64(HNullCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
@@ -77,13 +77,12 @@ class NullCheckSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathX86_64"; }
private:
- HNullCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
};
class DivZeroCheckSlowPathX86_64 : public SlowPathCode {
public:
- explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : instruction_(instruction) {}
+ explicit DivZeroCheckSlowPathX86_64(HDivZeroCheck* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
@@ -104,14 +103,13 @@ class DivZeroCheckSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathX86_64"; }
private:
- HDivZeroCheck* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathX86_64);
};
class DivRemMinusOneSlowPathX86_64 : public SlowPathCode {
public:
- DivRemMinusOneSlowPathX86_64(Register reg, Primitive::Type type, bool is_div)
- : cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {}
+ DivRemMinusOneSlowPathX86_64(HInstruction* at, Register reg, Primitive::Type type, bool is_div)
+ : SlowPathCode(at), cpu_reg_(CpuRegister(reg)), type_(type), is_div_(is_div) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
@@ -145,7 +143,7 @@ class DivRemMinusOneSlowPathX86_64 : public SlowPathCode {
class SuspendCheckSlowPathX86_64 : public SlowPathCode {
public:
SuspendCheckSlowPathX86_64(HSuspendCheck* instruction, HBasicBlock* successor)
- : instruction_(instruction), successor_(successor) {}
+ : SlowPathCode(instruction), successor_(successor) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
@@ -176,7 +174,6 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathX86_64"; }
private:
- HSuspendCheck* const instruction_;
HBasicBlock* const successor_;
Label return_label_;
@@ -186,7 +183,7 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode {
class BoundsCheckSlowPathX86_64 : public SlowPathCode {
public:
explicit BoundsCheckSlowPathX86_64(HBoundsCheck* instruction)
- : instruction_(instruction) {}
+ : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -218,8 +215,6 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathX86_64"; }
private:
- HBoundsCheck* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64);
};
@@ -229,7 +224,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode {
HInstruction* at,
uint32_t dex_pc,
bool do_clinit)
- : cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ : SlowPathCode(at), cls_(cls), at_(at), dex_pc_(dex_pc), do_clinit_(do_clinit) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -286,7 +281,7 @@ class LoadClassSlowPathX86_64 : public SlowPathCode {
class LoadStringSlowPathX86_64 : public SlowPathCode {
public:
- explicit LoadStringSlowPathX86_64(HLoadString* instruction) : instruction_(instruction) {}
+ explicit LoadStringSlowPathX86_64(HLoadString* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -297,8 +292,8 @@ class LoadStringSlowPathX86_64 : public SlowPathCode {
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
- __ movl(CpuRegister(calling_convention.GetRegisterAt(0)),
- Immediate(instruction_->GetStringIndex()));
+ const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex();
+ __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(string_index));
x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pResolveString),
instruction_,
instruction_->GetDexPc(),
@@ -312,15 +307,13 @@ class LoadStringSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathX86_64"; }
private:
- HLoadString* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathX86_64);
};
class TypeCheckSlowPathX86_64 : public SlowPathCode {
public:
TypeCheckSlowPathX86_64(HInstruction* instruction, bool is_fatal)
- : instruction_(instruction), is_fatal_(is_fatal) {}
+ : SlowPathCode(instruction), is_fatal_(is_fatal) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -379,7 +372,6 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode {
bool IsFatal() const OVERRIDE { return is_fatal_; }
private:
- HInstruction* const instruction_;
const bool is_fatal_;
DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathX86_64);
@@ -388,7 +380,7 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode {
class DeoptimizationSlowPathX86_64 : public SlowPathCode {
public:
explicit DeoptimizationSlowPathX86_64(HDeoptimize* instruction)
- : instruction_(instruction) {}
+ : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
@@ -404,13 +396,12 @@ class DeoptimizationSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathX86_64"; }
private:
- HDeoptimize* const instruction_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathX86_64);
};
class ArraySetSlowPathX86_64 : public SlowPathCode {
public:
- explicit ArraySetSlowPathX86_64(HInstruction* instruction) : instruction_(instruction) {}
+ explicit ArraySetSlowPathX86_64(HInstruction* instruction) : SlowPathCode(instruction) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
@@ -449,8 +440,6 @@ class ArraySetSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathX86_64"; }
private:
- HInstruction* const instruction_;
-
DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathX86_64);
};
@@ -458,7 +447,7 @@ class ArraySetSlowPathX86_64 : public SlowPathCode {
class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
public:
ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location out, Location obj)
- : instruction_(instruction), out_(out), obj_(obj) {
+ : SlowPathCode(instruction), out_(out), obj_(obj) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -497,7 +486,6 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
}
private:
- HInstruction* const instruction_;
const Location out_;
const Location obj_;
@@ -513,7 +501,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
Location obj,
uint32_t offset,
Location index)
- : instruction_(instruction),
+ : SlowPathCode(instruction),
out_(out),
ref_(ref),
obj_(obj),
@@ -667,7 +655,6 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
UNREACHABLE();
}
- HInstruction* const instruction_;
const Location out_;
const Location ref_;
const Location obj_;
@@ -684,7 +671,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode {
class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode {
public:
ReadBarrierForRootSlowPathX86_64(HInstruction* instruction, Location out, Location root)
- : instruction_(instruction), out_(out), root_(root) {
+ : SlowPathCode(instruction), out_(out), root_(root) {
DCHECK(kEmitCompilerReadBarrier);
}
@@ -716,7 +703,6 @@ class ReadBarrierForRootSlowPathX86_64 : public SlowPathCode {
const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathX86_64"; }
private:
- HInstruction* const instruction_;
const Location out_;
const Location root_;
@@ -1632,11 +1618,11 @@ void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
}
void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- if (codegen_->HasStackMapAtCurrentPc()) {
- // Ensure that we do not collide with the stack map of the previous instruction.
- __ nop();
- }
- codegen_->RecordPcInfo(info, info->GetDexPc());
+ codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc());
+}
+
+void CodeGeneratorX86_64::GenerateNop() {
+ __ nop();
}
void LocationsBuilderX86_64::VisitLocal(HLocal* local) {
@@ -3546,7 +3532,7 @@ void InstructionCodeGeneratorX86_64::GenerateDivRemIntegral(HBinaryOperation* in
} else {
SlowPathCode* slow_path =
new (GetGraph()->GetArena()) DivRemMinusOneSlowPathX86_64(
- out.AsRegister(), type, is_div);
+ instruction, out.AsRegister(), type, is_div);
codegen_->AddSlowPath(slow_path);
CpuRegister second_reg = second.AsRegister<CpuRegister>();
@@ -6511,8 +6497,8 @@ void CodeGeneratorX86_64::Load64BitValue(CpuRegister dest, int64_t value) {
if (value == 0) {
// Clears upper bits too.
__ xorl(dest, dest);
- } else if (value > 0 && IsInt<32>(value)) {
- // We can use a 32 bit move, as it will zero-extend and is one byte shorter.
+ } else if (IsUint<32>(value)) {
+ // We can use a 32 bit move, as it will zero-extend and is shorter.
__ movl(dest, Immediate(static_cast<int32_t>(value)));
} else {
__ movq(dest, Immediate(value));
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 97f6f84236..b3d27e194a 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -513,6 +513,8 @@ class CodeGeneratorX86_64 : public CodeGenerator {
}
}
+ void GenerateNop();
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index e0bc6f73dc..e10b1d6b2e 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -39,7 +39,7 @@ namespace art {
*/
class HConstantFolding : public HOptimization {
public:
- explicit HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName)
+ HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName)
: HOptimization(graph, name) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index e6e9177841..4a49c83611 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -593,8 +593,9 @@ void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
HBasicBlock* predecessor = loop_header->GetPredecessors()[i];
if (!loop_information->IsBackEdge(*predecessor)) {
AddError(StringPrintf(
- "Loop header %d has multiple incoming (non back edge) blocks.",
- id));
+ "Loop header %d has multiple incoming (non back edge) blocks: %d.",
+ id,
+ predecessor->GetBlockId()));
}
}
}
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 37f2d79536..a1e1cde9df 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -379,7 +379,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(Inducti
Primitive::Type type) {
// Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
int64_t value = -1;
- if (a != nullptr && IsIntAndGet(b, &value)) {
+ if (a != nullptr && IsExact(b, &value)) {
// Obtain the constant needed for the multiplication. This yields an existing instruction
// if the constants is already there. Otherwise, this has a side effect on the HIR.
// The restriction on the shift factor avoids generating a negative constant
@@ -546,9 +546,10 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
// Analyze condition with induction at left-hand-side (e.g. i < U).
InductionInfo* lower_expr = a->op_b;
InductionInfo* upper_expr = b;
- InductionInfo* stride = a->op_a;
+ InductionInfo* stride_expr = a->op_a;
+ // Constant stride?
int64_t stride_value = 0;
- if (!IsIntAndGet(stride, &stride_value)) {
+ if (!IsExact(stride_expr, &stride_value)) {
return;
}
// Rewrite condition i != U into i < U or i > U if end condition is reached exactly.
@@ -561,7 +562,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
// stride < 0, either i > U or i >= U
if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
(stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
- VisitTripCount(loop, lower_expr, upper_expr, stride, stride_value, type, cmp);
+ VisitTripCount(loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp);
}
}
}
@@ -569,7 +570,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
InductionInfo* lower_expr,
InductionInfo* upper_expr,
- InductionInfo* stride,
+ InductionInfo* stride_expr,
int64_t stride_value,
Primitive::Type type,
IfCondition cmp) {
@@ -612,9 +613,10 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
trip_count = CreateInvariantOp(kAdd, trip_count, CreateConstant(1, type));
}
// Compensate for stride.
- trip_count = CreateInvariantOp(kAdd, trip_count, stride);
+ trip_count = CreateInvariantOp(kAdd, trip_count, stride_expr);
}
- trip_count = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride);
+ trip_count = CreateInvariantOp(
+ kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride_expr);
// Assign the trip-count expression to the loop control. Clients that use the information
// should be aware that the expression is only valid under the conditions listed above.
InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests
@@ -644,14 +646,25 @@ bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr,
IfCondition cmp) {
int64_t lower_value;
int64_t upper_value;
- if (IsIntAndGet(lower_expr, &lower_value) && IsIntAndGet(upper_expr, &upper_value)) {
- switch (cmp) {
- case kCondLT: return lower_value < upper_value;
- case kCondLE: return lower_value <= upper_value;
- case kCondGT: return lower_value > upper_value;
- case kCondGE: return lower_value >= upper_value;
- default: LOG(FATAL) << "CONDITION UNREACHABLE";
- }
+ switch (cmp) {
+ case kCondLT:
+ return IsAtMost(lower_expr, &lower_value)
+ && IsAtLeast(upper_expr, &upper_value)
+ && lower_value < upper_value;
+ case kCondLE:
+ return IsAtMost(lower_expr, &lower_value)
+ && IsAtLeast(upper_expr, &upper_value)
+ && lower_value <= upper_value;
+ case kCondGT:
+ return IsAtLeast(lower_expr, &lower_value)
+ && IsAtMost(upper_expr, &upper_value)
+ && lower_value > upper_value;
+ case kCondGE:
+ return IsAtLeast(lower_expr, &lower_value)
+ && IsAtMost(upper_expr, &upper_value)
+ && lower_value >= upper_value;
+ default:
+ LOG(FATAL) << "CONDITION UNREACHABLE";
}
return false; // not certain, may be untaken
}
@@ -660,25 +673,23 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr,
int64_t stride_value,
Primitive::Type type,
IfCondition cmp) {
- const int64_t min = type == Primitive::kPrimInt
- ? std::numeric_limits<int32_t>::min()
- : std::numeric_limits<int64_t>::min();
- const int64_t max = type == Primitive::kPrimInt
- ? std::numeric_limits<int32_t>::max()
- : std::numeric_limits<int64_t>::max();
+ const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::min()
+ : std::numeric_limits<int64_t>::min();
+ const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::max()
+ : std::numeric_limits<int64_t>::max();
// Some rules under which it is certain at compile-time that the loop is finite.
int64_t value;
switch (cmp) {
case kCondLT:
return stride_value == 1 ||
- (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value + 1));
+ (IsAtMost(upper_expr, &value) && value <= (max - stride_value + 1));
case kCondLE:
- return (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value));
+ return (IsAtMost(upper_expr, &value) && value <= (max - stride_value));
case kCondGT:
return stride_value == -1 ||
- (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value - 1));
+ (IsAtLeast(upper_expr, &value) && value >= (min - stride_value - 1));
case kCondGE:
- return (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value));
+ return (IsAtLeast(upper_expr, &value) && value >= (min - stride_value));
default:
LOG(FATAL) << "CONDITION UNREACHABLE";
}
@@ -733,7 +744,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
// More exhaustive simplifications are done by later phases once induction nodes are
// translated back into HIR code (e.g. by loop optimizations or BCE).
int64_t value = -1;
- if (IsIntAndGet(a, &value)) {
+ if (IsExact(a, &value)) {
if (value == 0) {
// Simplify 0 + b = b, 0 * b = 0.
if (op == kAdd) {
@@ -750,7 +761,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
}
}
}
- if (IsIntAndGet(b, &value)) {
+ if (IsExact(b, &value)) {
if (value == 0) {
// Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0.
if (op == kAdd || op == kSub) {
@@ -784,29 +795,16 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
}
-bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) {
- if (info != nullptr && info->induction_class == kInvariant) {
- // A direct constant fetch.
- if (info->operation == kFetch) {
- DCHECK(info->fetch);
- if (info->fetch->IsIntConstant()) {
- *value = info->fetch->AsIntConstant()->GetValue();
- return true;
- } else if (info->fetch->IsLongConstant()) {
- *value = info->fetch->AsLongConstant()->GetValue();
- return true;
- }
- }
- // Use range analysis to resolve compound values.
- InductionVarRange range(this);
- int32_t min_val = 0;
- int32_t max_val = 0;
- if (range.IsConstantRange(info, &min_val, &max_val) && min_val == max_val) {
- *value = min_val;
- return true;
- }
- }
- return false;
+bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) {
+ return InductionVarRange(this).IsConstant(info, InductionVarRange::kExact, value);
+}
+
+bool HInductionVarAnalysis::IsAtMost(InductionInfo* info, int64_t* value) {
+ return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtMost, value);
+}
+
+bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) {
+ return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value);
}
bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 84d5d82568..94d2646aec 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -189,7 +189,9 @@ class HInductionVarAnalysis : public HOptimization {
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
// Constants.
- bool IsIntAndGet(InductionInfo* info, int64_t* value);
+ bool IsExact(InductionInfo* info, /*out*/ int64_t* value);
+ bool IsAtMost(InductionInfo* info, /*out*/ int64_t* value);
+ bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value);
// Helpers.
static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 9566c29adf..b162696a42 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -45,17 +45,14 @@ static bool IsSafeDiv(int32_t c1, int32_t c2) {
return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
}
-/** Returns true for 32/64-bit integral constant. */
-static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
+/** Returns true for 32/64-bit constant instruction. */
+static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
if (instruction->IsIntConstant()) {
*value = instruction->AsIntConstant()->GetValue();
return true;
} else if (instruction->IsLongConstant()) {
- const int64_t c = instruction->AsLongConstant()->GetValue();
- if (CanLongValueFitIntoInt(c)) {
- *value = static_cast<int32_t>(c);
- return true;
- }
+ *value = instruction->AsLongConstant()->GetValue();
+ return true;
}
return false;
}
@@ -65,8 +62,9 @@ static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
* because length >= 0 is true. This makes it more likely the bound is useful to clients.
*/
static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
- int32_t value;
- if (v.a_constant > 1 &&
+ int64_t value;
+ if (v.is_known &&
+ v.a_constant > 1 &&
v.instruction->IsDiv() &&
v.instruction->InputAt(0)->IsArrayLength() &&
IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
@@ -75,6 +73,16 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
return v;
}
+/** Helper method to test for a constant value. */
+static bool IsConstantValue(InductionVarRange::Value v) {
+ return v.is_known && v.a_constant == 0;
+}
+
+/** Helper method to test for same constant value. */
+static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) {
+ return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant;
+}
+
/** Helper method to insert an instruction. */
static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
DCHECK(block != nullptr);
@@ -99,29 +107,45 @@ bool InductionVarRange::GetInductionRange(HInstruction* context,
/*out*/Value* max_val,
/*out*/bool* needs_finite_test) {
HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop != nullptr) {
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = context->GetBlock() != header;
- HInductionVarAnalysis::InductionInfo* info =
- induction_analysis_->LookupInfo(loop, instruction);
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
- // Find range.
- *min_val = GetVal(info, trip, in_body, /* is_min */ true);
- *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
- *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
- return true;
+ if (loop == nullptr) {
+ return false; // no loop
+ }
+ HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
+ if (info == nullptr) {
+ return false; // no induction information
}
- return false; // Nothing known
+ // Set up loop information.
+ HBasicBlock* header = loop->GetHeader();
+ bool in_body = context->GetBlock() != header;
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+ // Find range.
+ *min_val = GetVal(info, trip, in_body, /* is_min */ true);
+ *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
+ *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
+ return true;
}
-bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const {
- Value v1 = RefineOuter(*min_val, /* is_min */ true);
- Value v2 = RefineOuter(*max_val, /* is_min */ false);
- if (v1.instruction != min_val->instruction || v2.instruction != max_val->instruction) {
- *min_val = v1;
- *max_val = v2;
+bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val,
+ /*in-out*/ Value* max_val) const {
+ Value v1_min = RefineOuter(*min_val, /* is_min */ true);
+ Value v2_max = RefineOuter(*max_val, /* is_min */ false);
+ // The refined range is safe if both sides refine the same instruction. Otherwise, since two
+ // different ranges are combined, the new refined range is safe to pass back to the client if
+ // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur.
+ if (min_val->instruction != max_val->instruction) {
+ Value v1_max = RefineOuter(*min_val, /* is_min */ false);
+ Value v2_min = RefineOuter(*max_val, /* is_min */ true);
+ if (!IsConstantValue(v1_max) ||
+ !IsConstantValue(v2_min) ||
+ v1_max.b_constant > v2_min.b_constant) {
+ return false;
+ }
+ }
+ // Did something change?
+ if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) {
+ *min_val = v1_min;
+ *max_val = v2_max;
return true;
}
return false;
@@ -164,6 +188,38 @@ void InductionVarRange::GenerateTakenTest(HInstruction* context,
// Private class methods.
//
+bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
+ ConstantRequest request,
+ /*out*/ int64_t *value) const {
+ if (info != nullptr) {
+ // A direct 32-bit or 64-bit constant fetch. This immediately satisfies
+ // any of the three requests (kExact, kAtMost, and KAtLeast).
+ if (info->induction_class == HInductionVarAnalysis::kInvariant &&
+ info->operation == HInductionVarAnalysis::kFetch) {
+ if (IsIntAndGet(info->fetch, value)) {
+ return true;
+ }
+ }
+ // Try range analysis while traversing outward on loops.
+ bool in_body = true; // no known trip count
+ Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
+ Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
+ do {
+ // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies.
+ if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) {
+ if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) {
+ *value = v_max.b_constant;
+ return true;
+ } else if (request == kAtLeast) {
+ *value = v_min.b_constant;
+ return true;
+ }
+ }
+ } while (RefineOuter(&v_min, &v_max));
+ }
+ return false;
+}
+
bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const {
if (info != nullptr) {
if (info->induction_class == HInductionVarAnalysis::kLinear) {
@@ -206,12 +262,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind
if (trip != nullptr) {
HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
if (trip_expr->operation == HInductionVarAnalysis::kSub) {
- int32_t min_value = 0;
- int32_t stride_value = 0;
- if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) {
+ int64_t stride_value = 0;
+ if (IsConstant(info->op_a, kExact, &stride_value)) {
if (!is_min && stride_value == 1) {
- // Test original trip's negative operand (trip_expr->op_b) against
- // the offset of the linear induction.
+ // Test original trip's negative operand (trip_expr->op_b) against offset of induction.
if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) {
// Analyze cancelled trip with just the positive operand (trip_expr->op_a).
HInductionVarAnalysis::InductionInfo cancelled_trip(
@@ -219,8 +273,7 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind
return GetVal(&cancelled_trip, trip, in_body, is_min);
}
} else if (is_min && stride_value == -1) {
- // Test original trip's positive operand (trip_expr->op_a) against
- // the offset of the linear induction.
+ // Test original trip's positive operand (trip_expr->op_a) against offset of induction.
if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
// Analyze cancelled trip with just the negative operand (trip_expr->op_b).
HInductionVarAnalysis::InductionInfo neg(
@@ -248,14 +301,16 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
bool is_min) const {
// Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
// more likely range analysis will compare the same instructions as terminal nodes.
- int32_t value;
- if (IsIntAndGet(instruction, &value)) {
- return Value(value);
+ int64_t value;
+ if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
+ return Value(static_cast<int32_t>(value));
} else if (instruction->IsAdd()) {
- if (IsIntAndGet(instruction->InputAt(0), &value)) {
- return AddValue(Value(value), GetFetch(instruction->InputAt(1), trip, in_body, is_min));
- } else if (IsIntAndGet(instruction->InputAt(1), &value)) {
- return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), Value(value));
+ if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
+ return AddValue(Value(static_cast<int32_t>(value)),
+ GetFetch(instruction->InputAt(1), trip, in_body, is_min));
+ } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
+ return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min),
+ Value(static_cast<int32_t>(value)));
}
} else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
@@ -331,29 +386,30 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
- // Try to refine certain failure.
- if (v1_min.a_constant && v1_max.a_constant) {
- v1_min = RefineOuter(v1_min, /* is_min */ true);
- v1_max = RefineOuter(v1_max, /* is_min */ false);
- }
- // Positive or negative range?
- if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
- // Positive range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? MulValue(v1_min, v2_min)
- : MulValue(v1_max, v2_max);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? MulValue(v1_max, v2_min)
- : MulValue(v1_min, v2_max);
+ // Try to refine first operand.
+ if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) {
+ RefineOuter(&v1_min, &v1_max);
+ }
+ // Constant times range.
+ if (IsSameConstantValue(v1_min, v1_max)) {
+ return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min);
+ } else if (IsSameConstantValue(v2_min, v2_max)) {
+ return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min);
+ }
+ // Positive range vs. positive or negative range.
+ if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max);
}
- } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) {
- // Negative range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? MulValue(v1_min, v2_max)
- : MulValue(v1_max, v2_min);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? MulValue(v1_max, v2_max)
- : MulValue(v1_min, v2_min);
+ }
+ // Negative range vs. positive or negative range.
+ if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min);
}
}
return Value();
@@ -368,43 +424,41 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
- // Positive or negative range?
- if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
- // Positive range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? DivValue(v1_min, v2_max)
- : DivValue(v1_max, v2_min);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? DivValue(v1_max, v2_max)
- : DivValue(v1_min, v2_min);
+ // Range divided by constant.
+ if (IsSameConstantValue(v2_min, v2_max)) {
+ return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min);
+ }
+ // Positive range vs. positive or negative range.
+ if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min);
}
- } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) {
- // Negative range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? DivValue(v1_min, v2_min)
- : DivValue(v1_max, v2_max);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? DivValue(v1_max, v2_min)
- : DivValue(v1_min, v2_max);
+ }
+ // Negative range vs. positive or negative range.
+ if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max);
}
}
return Value();
}
-bool InductionVarRange::IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
- int32_t *min_value,
- int32_t *max_value) const {
- bool in_body = true; // no known trip count
- Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
- Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
- do {
- if (v_min.is_known && v_min.a_constant == 0 && v_max.is_known && v_max.a_constant == 0) {
- *min_value = v_min.b_constant;
- *max_value = v_max.b_constant;
- return true;
- }
- } while (RefineOuter(&v_min, &v_max));
- return false;
+InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min,
+ Value v_max,
+ Value c,
+ bool is_min) const {
+ return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c);
+}
+
+InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min,
+ Value v_max,
+ Value c,
+ bool is_min) const {
+ return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c);
}
InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
@@ -471,22 +525,25 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is
}
InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const {
- if (v.instruction != nullptr) {
- HLoopInformation* loop =
- v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop != nullptr) {
- // Set up loop information.
- bool in_body = true; // use is always in body of outer loop
- HInductionVarAnalysis::InductionInfo* info =
- induction_analysis_->LookupInfo(loop, v.instruction);
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction());
- // Try to refine "a x instruction + b" with outer loop range information on instruction.
- return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)),
- Value(v.b_constant));
- }
+ if (v.instruction == nullptr) {
+ return v; // nothing to refine
}
- return v;
+ HLoopInformation* loop =
+ v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
+ if (loop == nullptr) {
+ return v; // no loop
+ }
+ HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction);
+ if (info == nullptr) {
+ return v; // no induction information
+ }
+ // Set up loop information.
+ HBasicBlock* header = loop->GetHeader();
+ bool in_body = true; // inner always in more outer
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+ // Try to refine "a x instruction + b" with outer loop range information on instruction.
+ return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant));
}
bool InductionVarRange::GenerateCode(HInstruction* context,
@@ -499,44 +556,45 @@ bool InductionVarRange::GenerateCode(HInstruction* context,
/*out*/bool* needs_finite_test,
/*out*/bool* needs_taken_test) const {
HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop != nullptr) {
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = context->GetBlock() != header;
- HInductionVarAnalysis::InductionInfo* info =
- induction_analysis_->LookupInfo(loop, instruction);
- if (info == nullptr) {
- return false; // nothing to analyze
- }
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
- // Determine what tests are needed. A finite test is needed if the evaluation code uses the
- // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
- // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
- // code does not use the trip-count explicitly (since there could be an implicit relation
- // between e.g. an invariant subscript and a not-taken condition).
- *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
- *needs_taken_test = IsBodyTripCount(trip);
- // Code generation for taken test: generate the code when requested or otherwise analyze
- // if code generation is feasible when taken test is needed.
- if (taken_test != nullptr) {
- return GenerateCode(
- trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false);
- } else if (*needs_taken_test) {
- if (!GenerateCode(
- trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) {
- return false;
- }
+ if (loop == nullptr) {
+ return false; // no loop
+ }
+ HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
+ if (info == nullptr) {
+ return false; // no induction information
+ }
+ // Set up loop information.
+ HBasicBlock* header = loop->GetHeader();
+ bool in_body = context->GetBlock() != header;
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+ if (trip == nullptr) {
+ return false; // codegen relies on trip count
+ }
+ // Determine what tests are needed. A finite test is needed if the evaluation code uses the
+ // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
+ // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
+ // code does not use the trip-count explicitly (since there could be an implicit relation
+ // between e.g. an invariant subscript and a not-taken condition).
+ *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
+ *needs_taken_test = IsBodyTripCount(trip);
+ // Code generation for taken test: generate the code when requested or otherwise analyze
+ // if code generation is feasible when taken test is needed.
+ if (taken_test != nullptr) {
+ return GenerateCode(trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false);
+ } else if (*needs_taken_test) {
+ if (!GenerateCode(
+ trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) {
+ return false;
}
- // Code generation for lower and upper.
- return
- // Success on lower if invariant (not set), or code can be generated.
- ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
- GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) &&
- // And success on upper.
- GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
}
- return false;
+ // Code generation for lower and upper.
+ return
+ // Success on lower if invariant (not set), or code can be generated.
+ ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
+ GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) &&
+ // And success on upper.
+ GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
}
bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
@@ -639,9 +697,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
case HInductionVarAnalysis::kLinear: {
// Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only
// to avoid arithmetic wrap-around situations that are hard to guard against.
- int32_t min_value = 0;
- int32_t stride_value = 0;
- if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) {
+ int64_t stride_value = 0;
+ if (IsConstant(info->op_a, kExact, &stride_value)) {
if (stride_value == 1 || stride_value == -1) {
const bool is_min_a = stride_value == 1 ? is_min : !is_min;
if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
@@ -666,7 +723,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
// Wrap-around and periodic inductions are restricted to constants only, so that extreme
// values are easy to test at runtime without complications of arithmetic wrap-around.
Value extreme = GetVal(info, trip, in_body, is_min);
- if (extreme.is_known && extreme.a_constant == 0) {
+ if (IsConstantValue(extreme)) {
if (graph != nullptr) {
*result = graph->GetIntConstant(extreme.b_constant);
}
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 3cb7b4bfd5..0af41560ff 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -69,7 +69,8 @@ class InductionVarRange {
/*out*/ bool* needs_finite_test);
/** Refines the values with induction of next outer loop. Returns true on change. */
- bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const;
+ bool RefineOuter(/*in-out*/ Value* min_val,
+ /*in-out*/ Value* max_val) const;
/**
* Returns true if range analysis is able to generate code for the lower and upper
@@ -116,6 +117,23 @@ class InductionVarRange {
/*out*/ HInstruction** taken_test);
private:
+ /*
+ * Enum used in IsConstant() request.
+ */
+ enum ConstantRequest {
+ kExact,
+ kAtMost,
+ kAtLeast
+ };
+
+ /**
+ * Returns true if exact or upper/lower bound on the given induction
+ * information is known as a 64-bit constant, which is returned in value.
+ */
+ bool IsConstant(HInductionVarAnalysis::InductionInfo* info,
+ ConstantRequest request,
+ /*out*/ int64_t *value) const;
+
bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const;
bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
@@ -143,9 +161,8 @@ class InductionVarRange {
bool in_body,
bool is_min) const;
- bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
- int32_t *min_value,
- int32_t *max_value) const;
+ Value MulRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const;
+ Value DivRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const;
Value AddValue(Value v1, Value v2) const;
Value SubValue(Value v1, Value v2) const;
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 55a654e301..c5c33bd9bc 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -215,10 +215,16 @@ class InductionVarRangeTest : public CommonCompilerTest {
return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min);
}
- bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
- int32_t* min_value,
- int32_t* max_value) {
- return range_.IsConstantRange(info, min_value, max_value);
+ bool IsExact(HInductionVarAnalysis::InductionInfo* info, int64_t* value) {
+ return range_.IsConstant(info, InductionVarRange::kExact, value);
+ }
+
+ bool IsAtMost(HInductionVarAnalysis::InductionInfo* info, int64_t* value) {
+ return range_.IsConstant(info, InductionVarRange::kAtMost, value);
+ }
+
+ bool IsAtLeast(HInductionVarAnalysis::InductionInfo* info, int64_t* value) {
+ return range_.IsConstant(info, InductionVarRange::kAtLeast, value);
}
Value AddValue(Value v1, Value v2) { return range_.AddValue(v1, v2); }
@@ -249,6 +255,34 @@ class InductionVarRangeTest : public CommonCompilerTest {
// Tests on private methods.
//
+TEST_F(InductionVarRangeTest, IsConstant) {
+ int64_t value;
+ // Constant.
+ EXPECT_TRUE(IsExact(CreateConst(12345), &value));
+ EXPECT_EQ(12345, value);
+ EXPECT_TRUE(IsAtMost(CreateConst(12345), &value));
+ EXPECT_EQ(12345, value);
+ EXPECT_TRUE(IsAtLeast(CreateConst(12345), &value));
+ EXPECT_EQ(12345, value);
+ // Constant trivial range.
+ EXPECT_TRUE(IsExact(CreateRange(111, 111), &value));
+ EXPECT_EQ(111, value);
+ EXPECT_TRUE(IsAtMost(CreateRange(111, 111), &value));
+ EXPECT_EQ(111, value);
+ EXPECT_TRUE(IsAtLeast(CreateRange(111, 111), &value));
+ EXPECT_EQ(111, value);
+ // Constant non-trivial range.
+ EXPECT_FALSE(IsExact(CreateRange(11, 22), &value));
+ EXPECT_TRUE(IsAtMost(CreateRange(11, 22), &value));
+ EXPECT_EQ(22, value);
+ EXPECT_TRUE(IsAtLeast(CreateRange(11, 22), &value));
+ EXPECT_EQ(11, value);
+ // Symbolic.
+ EXPECT_FALSE(IsExact(CreateFetch(x_), &value));
+ EXPECT_FALSE(IsAtMost(CreateFetch(x_), &value));
+ EXPECT_FALSE(IsAtLeast(CreateFetch(x_), &value));
+}
+
TEST_F(InductionVarRangeTest, TripCountProperties) {
EXPECT_FALSE(NeedsTripCount(nullptr));
EXPECT_FALSE(NeedsTripCount(CreateConst(1)));
@@ -367,6 +401,10 @@ TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
}
TEST_F(InductionVarRangeTest, GetMulMin) {
+ ExpectEqual(Value(-14), GetMul(CreateConst(2), CreateRange(-7, 8), true));
+ ExpectEqual(Value(-16), GetMul(CreateConst(-2), CreateRange(-7, 8), true));
+ ExpectEqual(Value(-14), GetMul(CreateRange(-7, 8), CreateConst(2), true));
+ ExpectEqual(Value(-16), GetMul(CreateRange(-7, 8), CreateConst(-2), true));
ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), true));
ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), true));
ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), true));
@@ -379,6 +417,10 @@ TEST_F(InductionVarRangeTest, GetMulMin) {
}
TEST_F(InductionVarRangeTest, GetMulMax) {
+ ExpectEqual(Value(16), GetMul(CreateConst(2), CreateRange(-7, 8), false));
+ ExpectEqual(Value(14), GetMul(CreateConst(-2), CreateRange(-7, 8), false));
+ ExpectEqual(Value(16), GetMul(CreateRange(-7, 8), CreateConst(2), false));
+ ExpectEqual(Value(14), GetMul(CreateRange(-7, 8), CreateConst(-2), false));
ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), false));
ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), false));
ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), false));
@@ -391,6 +433,8 @@ TEST_F(InductionVarRangeTest, GetMulMax) {
}
TEST_F(InductionVarRangeTest, GetDivMin) {
+ ExpectEqual(Value(-5), GetDiv(CreateRange(-10, 20), CreateConst(2), true));
+ ExpectEqual(Value(-10), GetDiv(CreateRange(-10, 20), CreateConst(-2), true));
ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), true));
ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), true));
ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), true));
@@ -403,6 +447,8 @@ TEST_F(InductionVarRangeTest, GetDivMin) {
}
TEST_F(InductionVarRangeTest, GetDivMax) {
+ ExpectEqual(Value(10), GetDiv(CreateRange(-10, 20), CreateConst(2), false));
+ ExpectEqual(Value(5), GetDiv(CreateRange(-10, 20), CreateConst(-2), false));
ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), false));
ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), false));
ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), false));
@@ -414,18 +460,6 @@ TEST_F(InductionVarRangeTest, GetDivMax) {
ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false));
}
-TEST_F(InductionVarRangeTest, IsConstantRange) {
- int32_t min_value;
- int32_t max_value;
- ASSERT_TRUE(IsConstantRange(CreateConst(12345), &min_value, &max_value));
- EXPECT_EQ(12345, min_value);
- EXPECT_EQ(12345, max_value);
- ASSERT_TRUE(IsConstantRange(CreateRange(1, 2), &min_value, &max_value));
- EXPECT_EQ(1, min_value);
- EXPECT_EQ(2, max_value);
- EXPECT_FALSE(IsConstantRange(CreateFetch(x_), &min_value, &max_value));
-}
-
TEST_F(InductionVarRangeTest, AddValue) {
ExpectEqual(Value(110), AddValue(Value(10), Value(100)));
ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1)));
@@ -459,6 +493,24 @@ TEST_F(InductionVarRangeTest, MulValue) {
ExpectEqual(Value(), MulValue(Value(90000), Value(-90000))); // unsafe
}
+TEST_F(InductionVarRangeTest, MulValueSpecial) {
+ const int32_t min_value = std::numeric_limits<int32_t>::min();
+ const int32_t max_value = std::numeric_limits<int32_t>::max();
+
+ // Unsafe.
+ ExpectEqual(Value(), MulValue(Value(min_value), Value(min_value)));
+ ExpectEqual(Value(), MulValue(Value(min_value), Value(-1)));
+ ExpectEqual(Value(), MulValue(Value(min_value), Value(max_value)));
+ ExpectEqual(Value(), MulValue(Value(max_value), Value(max_value)));
+
+ // Safe.
+ ExpectEqual(Value(min_value), MulValue(Value(min_value), Value(1)));
+ ExpectEqual(Value(max_value), MulValue(Value(max_value), Value(1)));
+ ExpectEqual(Value(-max_value), MulValue(Value(max_value), Value(-1)));
+ ExpectEqual(Value(-1), MulValue(Value(1), Value(-1)));
+ ExpectEqual(Value(1), MulValue(Value(-1), Value(-1)));
+}
+
TEST_F(InductionVarRangeTest, DivValue) {
ExpectEqual(Value(25), DivValue(Value(100), Value(4)));
ExpectEqual(Value(), DivValue(Value(x_, 1, -4), Value(x_, 1, -1)));
@@ -468,6 +520,23 @@ TEST_F(InductionVarRangeTest, DivValue) {
ExpectEqual(Value(), DivValue(Value(1), Value(0))); // unsafe
}
+TEST_F(InductionVarRangeTest, DivValueSpecial) {
+ const int32_t min_value = std::numeric_limits<int32_t>::min();
+ const int32_t max_value = std::numeric_limits<int32_t>::max();
+
+ // Unsafe.
+ ExpectEqual(Value(), DivValue(Value(min_value), Value(-1)));
+
+ // Safe.
+ ExpectEqual(Value(1), DivValue(Value(min_value), Value(min_value)));
+ ExpectEqual(Value(1), DivValue(Value(max_value), Value(max_value)));
+ ExpectEqual(Value(min_value), DivValue(Value(min_value), Value(1)));
+ ExpectEqual(Value(max_value), DivValue(Value(max_value), Value(1)));
+ ExpectEqual(Value(-max_value), DivValue(Value(max_value), Value(-1)));
+ ExpectEqual(Value(-1), DivValue(Value(1), Value(-1)));
+ ExpectEqual(Value(1), DivValue(Value(-1), Value(-1)));
+}
+
TEST_F(InductionVarRangeTest, MinValue) {
ExpectEqual(Value(10), MinValue(Value(10), Value(100)));
ExpectEqual(Value(x_, 1, -4), MinValue(Value(x_, 1, -4), Value(x_, 1, -1)));
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index a5acab81ab..02a1acc240 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -190,28 +190,34 @@ static uint32_t FindMethodIndexIn(ArtMethod* method,
}
}
-static uint32_t FindClassIndexIn(mirror::Class* cls, const DexFile& dex_file)
+static uint32_t FindClassIndexIn(mirror::Class* cls,
+ const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache)
SHARED_REQUIRES(Locks::mutator_lock_) {
+ uint32_t index = DexFile::kDexNoIndex;
if (cls->GetDexCache() == nullptr) {
- DCHECK(cls->IsArrayClass());
- // TODO: find the class in `dex_file`.
- return DexFile::kDexNoIndex;
+ DCHECK(cls->IsArrayClass()) << PrettyClass(cls);
+ index = cls->FindTypeIndexInOtherDexFile(dex_file);
} else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) {
+ DCHECK(cls->IsProxyClass()) << PrettyClass(cls);
// TODO: deal with proxy classes.
- return DexFile::kDexNoIndex;
} else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
+ index = cls->GetDexTypeIndex();
+ } else {
+ index = cls->FindTypeIndexInOtherDexFile(dex_file);
+ }
+
+ if (index != DexFile::kDexNoIndex) {
// Update the dex cache to ensure the class is in. The generated code will
// consider it is. We make it safe by updating the dex cache, as other
// dex files might also load the class, and there is no guarantee the dex
// cache of the dex file of the class will be updated.
- if (cls->GetDexCache()->GetResolvedType(cls->GetDexTypeIndex()) == nullptr) {
- cls->GetDexCache()->SetResolvedType(cls->GetDexTypeIndex(), cls);
+ if (dex_cache->GetResolvedType(index) == nullptr) {
+ dex_cache->SetResolvedType(index, cls);
}
- return cls->GetDexTypeIndex();
- } else {
- // TODO: find the class in `dex_file`.
- return DexFile::kDexNoIndex;
}
+
+ return index;
}
bool HInliner::TryInline(HInvoke* invoke_instruction) {
@@ -303,7 +309,7 @@ HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
uint32_t dex_pc) const {
ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
- return new (graph_->GetArena()) HInstanceFieldGet(
+ HInstanceFieldGet* result = new (graph_->GetArena()) HInstanceFieldGet(
receiver,
Primitive::kPrimNot,
field->GetOffset(),
@@ -313,6 +319,9 @@ HInstanceFieldGet* HInliner::BuildGetReceiverClass(ClassLinker* class_linker,
*field->GetDexFile(),
handles_->NewHandle(field->GetDexCache()),
dex_pc);
+ // The class of a field is effectively final, and does not have any memory dependencies.
+ result->SetSideEffects(SideEffects::None());
+ return result;
}
bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
@@ -322,7 +331,8 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
<< invoke_instruction->DebugName();
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
- uint32_t class_index = FindClassIndexIn(ic.GetMonomorphicType(), caller_dex_file);
+ uint32_t class_index = FindClassIndexIn(
+ ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache());
if (class_index == DexFile::kDexNoIndex) {
VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
<< " from inline cache is not inlined because its class is not"
@@ -350,11 +360,39 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
}
// We successfully inlined, now add a guard.
+ bool is_referrer =
+ (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ AddTypeGuard(receiver,
+ cursor,
+ bb_cursor,
+ class_index,
+ is_referrer,
+ invoke_instruction,
+ /* with_deoptimization */ true);
+
+ // Run type propagation to get the guard typed, and eventually propagate the
+ // type of the receiver.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ rtp_fixup.Run();
+
+ MaybeRecordStat(kInlinedMonomorphicCall);
+ return true;
+}
+
+HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
+ HInstruction* cursor,
+ HBasicBlock* bb_cursor,
+ uint32_t class_index,
+ bool is_referrer,
+ HInstruction* invoke_instruction,
+ bool with_deoptimization) {
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
class_linker, receiver, invoke_instruction->GetDexPc());
- bool is_referrer =
- (ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ // Note that we will just compare the classes, so we don't need Java semantics access checks.
+ // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache.
HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
class_index,
caller_dex_file,
@@ -364,8 +402,6 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
/* is_in_dex_cache */ true);
HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
- HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
- compare, invoke_instruction->GetDexPc());
// TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(receiver_class, cursor);
@@ -374,16 +410,13 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
}
bb_cursor->InsertInstructionAfter(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
- bb_cursor->InsertInstructionAfter(deoptimize, compare);
- deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
-
- // Run type propagation to get the guard typed, and eventually propagate the
- // type of the receiver.
- ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
- rtp_fixup.Run();
-
- MaybeRecordStat(kInlinedMonomorphicCall);
- return true;
+ if (with_deoptimization) {
+ HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+ compare, invoke_instruction->GetDexPc());
+ bb_cursor->InsertInstructionAfter(deoptimize, compare);
+ deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ }
+ return compare;
}
bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
@@ -391,6 +424,174 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
const InlineCache& ic) {
DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
<< invoke_instruction->DebugName();
+
+ if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, ic)) {
+ return true;
+ }
+
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+
+ bool all_targets_inlined = true;
+ bool one_target_inlined = false;
+ for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+ if (ic.GetTypeAt(i) == nullptr) {
+ break;
+ }
+ ArtMethod* method = nullptr;
+ if (invoke_instruction->IsInvokeInterface()) {
+ method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
+ resolved_method, pointer_size);
+ } else {
+ DCHECK(invoke_instruction->IsInvokeVirtual());
+ method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
+ resolved_method, pointer_size);
+ }
+
+ HInstruction* receiver = invoke_instruction->InputAt(0);
+ HInstruction* cursor = invoke_instruction->GetPrevious();
+ HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
+
+ uint32_t class_index = FindClassIndexIn(
+ ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
+ HInstruction* return_replacement = nullptr;
+ if (class_index == DexFile::kDexNoIndex ||
+ !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ all_targets_inlined = false;
+ } else {
+ one_target_inlined = true;
+ bool is_referrer = (ic.GetTypeAt(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+
+ // If we have inlined all targets before, and this receiver is the last seen,
+ // we deoptimize instead of keeping the original invoke instruction.
+ bool deoptimize = all_targets_inlined &&
+ (i != InlineCache::kIndividualCacheSize - 1) &&
+ (ic.GetTypeAt(i + 1) == nullptr);
+ HInstruction* compare = AddTypeGuard(
+ receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
+ if (deoptimize) {
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+ // Because the inline cache data can be populated concurrently, we force the end of the
+ // iteration. Otherhwise, we could see a new receiver type.
+ break;
+ } else {
+ CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
+ }
+ }
+ }
+
+ if (!one_target_inlined) {
+ VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
+ << " from inline cache is not inlined because none"
+ << " of its targets could be inlined";
+ return false;
+ }
+ MaybeRecordStat(kInlinedPolymorphicCall);
+
+ // Run type propagation to get the guards typed.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ rtp_fixup.Run();
+ return true;
+}
+
+void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
+ HInstruction* return_replacement,
+ HInstruction* invoke_instruction) {
+ uint32_t dex_pc = invoke_instruction->GetDexPc();
+ HBasicBlock* cursor_block = compare->GetBlock();
+ HBasicBlock* original_invoke_block = invoke_instruction->GetBlock();
+ ArenaAllocator* allocator = graph_->GetArena();
+
+ // Spit the block after the compare: `cursor_block` will now be the start of the diamond,
+ // and the returned block is the start of the then branch (that could contain multiple blocks).
+ HBasicBlock* then = cursor_block->SplitAfterForInlining(compare);
+
+ // Split the block containing the invoke before and after the invoke. The returned block
+ // of the split before will contain the invoke and will be the otherwise branch of
+ // the diamond. The returned block of the split after will be the merge block
+ // of the diamond.
+ HBasicBlock* end_then = invoke_instruction->GetBlock();
+ HBasicBlock* otherwise = end_then->SplitBeforeForInlining(invoke_instruction);
+ HBasicBlock* merge = otherwise->SplitAfterForInlining(invoke_instruction);
+
+ // If the methods we are inlining return a value, we create a phi in the merge block
+ // that will have the `invoke_instruction and the `return_replacement` as inputs.
+ if (return_replacement != nullptr) {
+ HPhi* phi = new (allocator) HPhi(
+ allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke_instruction->GetType()), dex_pc);
+ merge->AddPhi(phi);
+ invoke_instruction->ReplaceWith(phi);
+ phi->AddInput(return_replacement);
+ phi->AddInput(invoke_instruction);
+ }
+
+ // Add the control flow instructions.
+ otherwise->AddInstruction(new (allocator) HGoto(dex_pc));
+ end_then->AddInstruction(new (allocator) HGoto(dex_pc));
+ cursor_block->AddInstruction(new (allocator) HIf(compare, dex_pc));
+
+ // Add the newly created blocks to the graph.
+ graph_->AddBlock(then);
+ graph_->AddBlock(otherwise);
+ graph_->AddBlock(merge);
+
+ // Set up successor (and implictly predecessor) relations.
+ cursor_block->AddSuccessor(otherwise);
+ cursor_block->AddSuccessor(then);
+ end_then->AddSuccessor(merge);
+ otherwise->AddSuccessor(merge);
+
+ // Set up dominance information.
+ then->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(then);
+ otherwise->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(otherwise);
+ merge->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(merge);
+
+ // Update the revert post order.
+ size_t index = IndexOfElement(graph_->reverse_post_order_, cursor_block);
+ MakeRoomFor(&graph_->reverse_post_order_, 1, index);
+ graph_->reverse_post_order_[++index] = then;
+ index = IndexOfElement(graph_->reverse_post_order_, end_then);
+ MakeRoomFor(&graph_->reverse_post_order_, 2, index);
+ graph_->reverse_post_order_[++index] = otherwise;
+ graph_->reverse_post_order_[++index] = merge;
+
+ // Set the loop information of the newly created blocks.
+ HLoopInformation* loop_info = cursor_block->GetLoopInformation();
+ if (loop_info != nullptr) {
+ then->SetLoopInformation(cursor_block->GetLoopInformation());
+ merge->SetLoopInformation(cursor_block->GetLoopInformation());
+ otherwise->SetLoopInformation(cursor_block->GetLoopInformation());
+ for (HLoopInformationOutwardIterator loop_it(*cursor_block);
+ !loop_it.Done();
+ loop_it.Advance()) {
+ loop_it.Current()->Add(then);
+ loop_it.Current()->Add(merge);
+ loop_it.Current()->Add(otherwise);
+ }
+ // In case the original invoke location was a back edge, we need to update
+ // the loop to now have the merge block as a back edge.
+ if (loop_info->IsBackEdge(*original_invoke_block)) {
+ loop_info->RemoveBackEdge(original_invoke_block);
+ loop_info->AddBackEdge(merge);
+ }
+ }
+
+ // Set the try/catch information of the newly created blocks.
+ then->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ merge->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ otherwise->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+}
+
+bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ const InlineCache& ic) {
// This optimization only works under JIT for now.
DCHECK(Runtime::Current()->UseJit());
if (graph_->GetInstructionSet() == kMips64) {
@@ -557,8 +758,9 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
if (!method->GetDeclaringClass()->IsVerified()) {
uint16_t class_def_idx = method->GetDeclaringClass()->GetDexClassDefIndex();
- if (!compiler_driver_->IsMethodVerifiedWithoutFailures(
- method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
+ if (Runtime::Current()->UseJit() ||
+ !compiler_driver_->IsMethodVerifiedWithoutFailures(
+ method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) {
VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
<< " couldn't be verified, so it cannot be inlined";
return false;
@@ -781,16 +983,16 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
Handle<mirror::DexCache> dex_cache(handles_->NewHandle(resolved_method->GetDexCache()));
DexCompilationUnit dex_compilation_unit(
- nullptr,
- caller_compilation_unit_.GetClassLoader(),
- class_linker,
- callee_dex_file,
- code_item,
- resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
- method_index,
- resolved_method->GetAccessFlags(),
- compiler_driver_->GetVerifiedMethod(&callee_dex_file, method_index),
- dex_cache);
+ nullptr,
+ caller_compilation_unit_.GetClassLoader(),
+ class_linker,
+ callee_dex_file,
+ code_item,
+ resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
+ method_index,
+ resolved_method->GetAccessFlags(),
+ /* verified_method */ nullptr,
+ dex_cache);
bool requires_ctor_barrier = false;
@@ -883,7 +1085,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
HConstantFolding fold(callee_graph);
HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
InstructionSimplifier simplify(callee_graph, stats_);
- IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
+ IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_, stats_);
HOptimization* optimizations[] = {
&intrinsics,
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 9dd9bf5ad8..cdb2167082 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -101,12 +101,18 @@ class HInliner : public HOptimization {
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Try to inline targets of a polymorphic call. Currently unimplemented.
+ // Try to inline targets of a polymorphic call.
bool TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ const InlineCache& ic)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+
HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
HInstruction* receiver,
uint32_t dex_pc) const
@@ -118,6 +124,57 @@ class HInliner : public HOptimization {
bool do_rtp)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Add a type guard on the given `receiver`. This will add to the graph:
+ // i0 = HFieldGet(receiver, klass)
+ // i1 = HLoadClass(class_index, is_referrer)
+ // i2 = HNotEqual(i0, i1)
+ //
+ // And if `with_deoptimization` is true:
+ // HDeoptimize(i2)
+ //
+ // The method returns the `HNotEqual`, that will be used for polymorphic inlining.
+ HInstruction* AddTypeGuard(HInstruction* receiver,
+ HInstruction* cursor,
+ HBasicBlock* bb_cursor,
+ uint32_t class_index,
+ bool is_referrer,
+ HInstruction* invoke_instruction,
+ bool with_deoptimization)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ /*
+ * Ad-hoc implementation for implementing a diamond pattern in the graph for
+ * polymorphic inlining:
+ * 1) `compare` becomes the input of the new `HIf`.
+ * 2) Everything up until `invoke_instruction` is in the then branch (could
+ * contain multiple blocks).
+ * 3) `invoke_instruction` is moved to the otherwise block.
+ * 4) If `return_replacement` is not null, the merge block will have
+ * a phi whose inputs are `return_replacement` and `invoke_instruction`.
+ *
+ * Before:
+ * Block1
+ * compare
+ * ...
+ * invoke_instruction
+ *
+ * After:
+ * Block1
+ * compare
+ * if
+ * / \
+ * / \
+ * Then block Otherwise block
+ * ... invoke_instruction
+ * \ /
+ * \ /
+ * Merge block
+ * phi(return_replacement, invoke_instruction)
+ */
+ void CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
+ HInstruction* return_replacement,
+ HInstruction* invoke_instruction);
+
HGraph* const outermost_graph_;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index db39bc8eec..316e86b4c9 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -570,6 +570,7 @@ void IntrinsicsRecognizer::Run() {
NeedsEnvironmentOrCache(intrinsic),
GetSideEffects(intrinsic),
GetExceptions(intrinsic));
+ MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
}
}
}
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 3bf3f7ffae..2ab50bb436 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -33,8 +33,8 @@ static constexpr bool kRoundIsPlusPointFive = false;
// Recognize intrinsics from HInvoke nodes.
class IntrinsicsRecognizer : public HOptimization {
public:
- IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver)
- : HOptimization(graph, kIntrinsicsRecognizerPassName),
+ IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kIntrinsicsRecognizerPassName, stats),
driver_(driver) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index ea8669fa18..8cbdcbbcaf 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1825,6 +1825,90 @@ void IntrinsicCodeGeneratorARM::VisitMathNextAfter(HInvoke* invoke) {
GenFPFPToFPCall(invoke, GetAssembler(), codegen_, kQuickNextAfter);
}
+void IntrinsicLocationsBuilderARM::VisitIntegerReverse(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerReverse(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register in = locations->InAt(0).AsRegister<Register>();
+
+ __ rbit(out, in);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongReverse(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongReverse(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ __ rbit(out_reg_lo, in_reg_hi);
+ __ rbit(out_reg_hi, in_reg_lo);
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerReverseBytes(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register in = locations->InAt(0).AsRegister<Register>();
+
+ __ rev(out, in);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongReverseBytes(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongReverseBytes(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ __ rev(out_reg_lo, in_reg_hi);
+ __ rev(out_reg_hi, in_reg_lo);
+}
+
+void IntrinsicLocationsBuilderARM::VisitShortReverseBytes(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register in = locations->InAt(0).AsRegister<Register>();
+
+ __ revsh(out, in);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1834,12 +1918,7 @@ void IntrinsicCodeGeneratorARM::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED)
}
UNIMPLEMENTED_INTRINSIC(IntegerBitCount)
-UNIMPLEMENTED_INTRINSIC(IntegerReverse)
-UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes)
UNIMPLEMENTED_INTRINSIC(LongBitCount)
-UNIMPLEMENTED_INTRINSIC(LongReverse)
-UNIMPLEMENTED_INTRINSIC(LongReverseBytes)
-UNIMPLEMENTED_INTRINSIC(ShortReverseBytes)
UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 8741fd284f..b5f15fe22d 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -99,7 +99,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorARM64* codegen) {
// restored!
class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 {
public:
- explicit IntrinsicSlowPathARM64(HInvoke* invoke) : invoke_(invoke) { }
+ explicit IntrinsicSlowPathARM64(HInvoke* invoke)
+ : SlowPathCodeARM64(invoke), invoke_(invoke) { }
void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
CodeGeneratorARM64* codegen = down_cast<CodeGeneratorARM64*>(codegen_in);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index c8629644b6..2f183c3a62 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -99,7 +99,7 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS* codegen) {
// restored!
class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : invoke_(invoke) { }
+ explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : SlowPathCodeMIPS(invoke), invoke_(invoke) { }
void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
CodeGeneratorMIPS* codegen = down_cast<CodeGeneratorMIPS*>(codegen_in);
@@ -407,7 +407,7 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerReverseBytes(HInvoke* invoke) {
Primitive::kPrimInt,
IsR2OrNewer(),
IsR6(),
- false,
+ /* reverseBits */ false,
GetAssembler());
}
@@ -421,7 +421,7 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverseBytes(HInvoke* invoke) {
Primitive::kPrimLong,
IsR2OrNewer(),
IsR6(),
- false,
+ /* reverseBits */ false,
GetAssembler());
}
@@ -435,7 +435,7 @@ void IntrinsicCodeGeneratorMIPS::VisitShortReverseBytes(HInvoke* invoke) {
Primitive::kPrimShort,
IsR2OrNewer(),
IsR6(),
- false,
+ /* reverseBits */ false,
GetAssembler());
}
@@ -475,7 +475,7 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* in
}
void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) {
- GenNumberOfLeadingZeroes(invoke->GetLocations(), false, IsR6(), GetAssembler());
+ GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ false, IsR6(), GetAssembler());
}
// int java.lang.Long.numberOfLeadingZeros(long i)
@@ -484,7 +484,7 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invok
}
void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
- GenNumberOfLeadingZeroes(invoke->GetLocations(), true, IsR6(), GetAssembler());
+ GenNumberOfLeadingZeroes(invoke->GetLocations(), /* is64bit */ true, IsR6(), GetAssembler());
}
static void GenNumberOfTrailingZeroes(LocationSummary* locations,
@@ -497,7 +497,6 @@ static void GenNumberOfTrailingZeroes(LocationSummary* locations,
Register in;
if (is64bit) {
- MipsLabel done;
Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
@@ -588,7 +587,11 @@ void IntrinsicLocationsBuilderMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* i
}
void IntrinsicCodeGeneratorMIPS::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
- GenNumberOfTrailingZeroes(invoke->GetLocations(), false, IsR6(), IsR2OrNewer(), GetAssembler());
+ GenNumberOfTrailingZeroes(invoke->GetLocations(),
+ /* is64bit */ false,
+ IsR6(),
+ IsR2OrNewer(),
+ GetAssembler());
}
// int java.lang.Long.numberOfTrailingZeros(long i)
@@ -597,7 +600,11 @@ void IntrinsicLocationsBuilderMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invo
}
void IntrinsicCodeGeneratorMIPS::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
- GenNumberOfTrailingZeroes(invoke->GetLocations(), true, IsR6(), IsR2OrNewer(), GetAssembler());
+ GenNumberOfTrailingZeroes(invoke->GetLocations(),
+ /* is64bit */ true,
+ IsR6(),
+ IsR2OrNewer(),
+ GetAssembler());
}
enum RotationDirection {
@@ -806,7 +813,7 @@ void IntrinsicCodeGeneratorMIPS::VisitIntegerReverse(HInvoke* invoke) {
Primitive::kPrimInt,
IsR2OrNewer(),
IsR6(),
- true,
+ /* reverseBits */ true,
GetAssembler());
}
@@ -820,10 +827,561 @@ void IntrinsicCodeGeneratorMIPS::VisitLongReverse(HInvoke* invoke) {
Primitive::kPrimLong,
IsR2OrNewer(),
IsR6(),
- true,
+ /* reverseBits */ true,
GetAssembler());
}
+static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+}
+
+static void MathAbsFP(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
+ FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister out = locations->Out().AsFpuRegister<FRegister>();
+
+ if (is64bit) {
+ __ AbsD(out, in);
+ } else {
+ __ AbsS(out, in);
+ }
+}
+
+// double java.lang.Math.abs(double)
+void IntrinsicLocationsBuilderMIPS::VisitMathAbsDouble(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAbsDouble(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
+}
+
+// float java.lang.Math.abs(float)
+void IntrinsicLocationsBuilderMIPS::VisitMathAbsFloat(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAbsFloat(HInvoke* invoke) {
+ MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
+}
+
+static void GenAbsInteger(LocationSummary* locations, bool is64bit, MipsAssembler* assembler) {
+ if (is64bit) {
+ Register in_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ // The comments in this section show the analogous operations which would
+ // be performed if we had 64-bit registers "in", and "out".
+ // __ Dsra32(AT, in, 31);
+ __ Sra(AT, in_hi, 31);
+ // __ Xor(out, in, AT);
+ __ Xor(TMP, in_lo, AT);
+ __ Xor(out_hi, in_hi, AT);
+ // __ Dsubu(out, out, AT);
+ __ Subu(out_lo, TMP, AT);
+ __ Sltu(TMP, out_lo, TMP);
+ __ Addu(out_hi, out_hi, TMP);
+ } else {
+ Register in = locations->InAt(0).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ __ Sra(AT, in, 31);
+ __ Xor(out, in, AT);
+ __ Subu(out, out, AT);
+ }
+}
+
+// int java.lang.Math.abs(int)
+void IntrinsicLocationsBuilderMIPS::VisitMathAbsInt(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAbsInt(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), /* is64bit */ false, GetAssembler());
+}
+
+// long java.lang.Math.abs(long)
+void IntrinsicLocationsBuilderMIPS::VisitMathAbsLong(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathAbsLong(HInvoke* invoke) {
+ GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler());
+}
+
+static void GenMinMaxFP(LocationSummary* locations,
+ bool is_min,
+ Primitive::Type type,
+ bool is_R6,
+ MipsAssembler* assembler) {
+ FRegister out = locations->Out().AsFpuRegister<FRegister>();
+ FRegister a = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister b = locations->InAt(1).AsFpuRegister<FRegister>();
+
+ if (is_R6) {
+ MipsLabel noNaNs;
+ MipsLabel done;
+ FRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
+
+ // When Java computes min/max it prefers a NaN to a number; the
+ // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
+ // the inputs is a NaN and the other is a valid number, the MIPS
+ // instruction will return the number; Java wants the NaN value
+ // returned. This is why there is extra logic preceding the use of
+ // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
+ // NaN, return the NaN, otherwise return the min/max.
+ if (type == Primitive::kPrimDouble) {
+ __ CmpUnD(FTMP, a, b);
+ __ Bc1eqz(FTMP, &noNaNs);
+
+ // One of the inputs is a NaN
+ __ CmpEqD(ftmp, a, a);
+ // If a == a then b is the NaN, otherwise a is the NaN.
+ __ SelD(ftmp, a, b);
+
+ if (ftmp != out) {
+ __ MovD(out, ftmp);
+ }
+
+ __ B(&done);
+
+ __ Bind(&noNaNs);
+
+ if (is_min) {
+ __ MinD(out, a, b);
+ } else {
+ __ MaxD(out, a, b);
+ }
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimFloat);
+ __ CmpUnS(FTMP, a, b);
+ __ Bc1eqz(FTMP, &noNaNs);
+
+ // One of the inputs is a NaN
+ __ CmpEqS(ftmp, a, a);
+ // If a == a then b is the NaN, otherwise a is the NaN.
+ __ SelS(ftmp, a, b);
+
+ if (ftmp != out) {
+ __ MovS(out, ftmp);
+ }
+
+ __ B(&done);
+
+ __ Bind(&noNaNs);
+
+ if (is_min) {
+ __ MinS(out, a, b);
+ } else {
+ __ MaxS(out, a, b);
+ }
+ }
+
+ __ Bind(&done);
+ } else {
+ MipsLabel ordered;
+ MipsLabel compare;
+ MipsLabel select;
+ MipsLabel done;
+
+ if (type == Primitive::kPrimDouble) {
+ __ CunD(a, b);
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimFloat);
+ __ CunS(a, b);
+ }
+ __ Bc1f(&ordered);
+
+ // a or b (or both) is a NaN. Return one, which is a NaN.
+ if (type == Primitive::kPrimDouble) {
+ __ CeqD(b, b);
+ } else {
+ __ CeqS(b, b);
+ }
+ __ B(&select);
+
+ __ Bind(&ordered);
+
+ // Neither is a NaN.
+ // a == b? (-0.0 compares equal with +0.0)
+ // If equal, handle zeroes, else compare further.
+ if (type == Primitive::kPrimDouble) {
+ __ CeqD(a, b);
+ } else {
+ __ CeqS(a, b);
+ }
+ __ Bc1f(&compare);
+
+ // a == b either bit for bit or one is -0.0 and the other is +0.0.
+ if (type == Primitive::kPrimDouble) {
+ __ MoveFromFpuHigh(TMP, a);
+ __ MoveFromFpuHigh(AT, b);
+ } else {
+ __ Mfc1(TMP, a);
+ __ Mfc1(AT, b);
+ }
+
+ if (is_min) {
+ // -0.0 prevails over +0.0.
+ __ Or(TMP, TMP, AT);
+ } else {
+ // +0.0 prevails over -0.0.
+ __ And(TMP, TMP, AT);
+ }
+
+ if (type == Primitive::kPrimDouble) {
+ __ Mfc1(AT, a);
+ __ Mtc1(AT, out);
+ __ MoveToFpuHigh(TMP, out);
+ } else {
+ __ Mtc1(TMP, out);
+ }
+ __ B(&done);
+
+ __ Bind(&compare);
+
+ if (type == Primitive::kPrimDouble) {
+ if (is_min) {
+ // return (a <= b) ? a : b;
+ __ ColeD(a, b);
+ } else {
+ // return (a >= b) ? a : b;
+ __ ColeD(b, a); // b <= a
+ }
+ } else {
+ if (is_min) {
+ // return (a <= b) ? a : b;
+ __ ColeS(a, b);
+ } else {
+ // return (a >= b) ? a : b;
+ __ ColeS(b, a); // b <= a
+ }
+ }
+
+ __ Bind(&select);
+
+ if (type == Primitive::kPrimDouble) {
+ __ MovtD(out, a);
+ __ MovfD(out, b);
+ } else {
+ __ MovtS(out, a);
+ __ MovfS(out, b);
+ }
+
+ __ Bind(&done);
+ }
+}
+
+static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kOutputOverlap);
+}
+
+// double java.lang.Math.min(double, double)
+void IntrinsicLocationsBuilderMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMinDoubleDouble(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ true,
+ Primitive::kPrimDouble,
+ IsR6(),
+ GetAssembler());
+}
+
+// float java.lang.Math.min(float, float)
+void IntrinsicLocationsBuilderMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMinFloatFloat(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ true,
+ Primitive::kPrimFloat,
+ IsR6(),
+ GetAssembler());
+}
+
+// double java.lang.Math.max(double, double)
+void IntrinsicLocationsBuilderMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMaxDoubleDouble(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ false,
+ Primitive::kPrimDouble,
+ IsR6(),
+ GetAssembler());
+}
+
+// float java.lang.Math.max(float, float)
+void IntrinsicLocationsBuilderMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
+ CreateFPFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMaxFloatFloat(HInvoke* invoke) {
+ GenMinMaxFP(invoke->GetLocations(),
+ /* is_min */ false,
+ Primitive::kPrimFloat,
+ IsR6(),
+ GetAssembler());
+}
+
+static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+ LocationSummary* locations = new (arena) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+static void GenMinMax(LocationSummary* locations,
+ bool is_min,
+ Primitive::Type type,
+ bool is_R6,
+ MipsAssembler* assembler) {
+ if (is_R6) {
+ // Some architectures, such as ARM and MIPS (prior to r6), have a
+ // conditional move instruction which only changes the target
+ // (output) register if the condition is true (MIPS prior to r6 had
+ // MOVF, MOVT, MOVN, and MOVZ). The SELEQZ and SELNEZ instructions
+ // always change the target (output) register. If the condition is
+ // true the output register gets the contents of the "rs" register;
+ // otherwise, the output register is set to zero. One consequence
+ // of this is that to implement something like "rd = c==0 ? rs : rt"
+ // MIPS64r6 needs to use a pair of SELEQZ/SELNEZ instructions.
+ // After executing this pair of instructions one of the output
+ // registers from the pair will necessarily contain zero. Then the
+ // code ORs the output registers from the SELEQZ/SELNEZ instructions
+ // to get the final result.
+ //
+ // The initial test to see if the output register is same as the
+ // first input register is needed to make sure that value in the
+ // first input register isn't clobbered before we've finished
+ // computing the output value. The logic in the corresponding else
+ // clause performs the same task but makes sure the second input
+ // register isn't clobbered in the event that it's the same register
+ // as the output register; the else clause also handles the case
+ // where the output register is distinct from both the first, and the
+ // second input registers.
+ if (type == Primitive::kPrimLong) {
+ Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
+ Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
+ Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ MipsLabel compare_done;
+
+ if (a_lo == b_lo) {
+ if (out_lo != a_lo) {
+ __ Move(out_lo, a_lo);
+ __ Move(out_hi, a_hi);
+ }
+ } else {
+ __ Slt(TMP, b_hi, a_hi);
+ __ Bne(b_hi, a_hi, &compare_done);
+
+ __ Sltu(TMP, b_lo, a_lo);
+
+ __ Bind(&compare_done);
+
+ if (is_min) {
+ __ Seleqz(AT, a_lo, TMP);
+ __ Selnez(out_lo, b_lo, TMP); // Safe even if out_lo == a_lo/b_lo
+ // because at this point we're
+ // done using a_lo/b_lo.
+ } else {
+ __ Selnez(AT, a_lo, TMP);
+ __ Seleqz(out_lo, b_lo, TMP); // ditto
+ }
+ __ Or(out_lo, out_lo, AT);
+ if (is_min) {
+ __ Seleqz(AT, a_hi, TMP);
+ __ Selnez(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi
+ } else {
+ __ Selnez(AT, a_hi, TMP);
+ __ Seleqz(out_hi, b_hi, TMP); // ditto but for out_hi & a_hi/b_hi
+ }
+ __ Or(out_hi, out_hi, AT);
+ }
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimInt);
+ Register a = locations->InAt(0).AsRegister<Register>();
+ Register b = locations->InAt(1).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ if (a == b) {
+ if (out != a) {
+ __ Move(out, a);
+ }
+ } else {
+ __ Slt(AT, b, a);
+ if (is_min) {
+ __ Seleqz(TMP, a, AT);
+ __ Selnez(AT, b, AT);
+ } else {
+ __ Selnez(TMP, a, AT);
+ __ Seleqz(AT, b, AT);
+ }
+ __ Or(out, TMP, AT);
+ }
+ }
+ } else {
+ if (type == Primitive::kPrimLong) {
+ Register a_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register a_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Register b_lo = locations->InAt(1).AsRegisterPairLow<Register>();
+ Register b_hi = locations->InAt(1).AsRegisterPairHigh<Register>();
+ Register out_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ MipsLabel compare_done;
+
+ if (a_lo == b_lo) {
+ if (out_lo != a_lo) {
+ __ Move(out_lo, a_lo);
+ __ Move(out_hi, a_hi);
+ }
+ } else {
+ __ Slt(TMP, a_hi, b_hi);
+ __ Bne(a_hi, b_hi, &compare_done);
+
+ __ Sltu(TMP, a_lo, b_lo);
+
+ __ Bind(&compare_done);
+
+ if (is_min) {
+ if (out_lo != a_lo) {
+ __ Movn(out_hi, a_hi, TMP);
+ __ Movn(out_lo, a_lo, TMP);
+ }
+ if (out_lo != b_lo) {
+ __ Movz(out_hi, b_hi, TMP);
+ __ Movz(out_lo, b_lo, TMP);
+ }
+ } else {
+ if (out_lo != a_lo) {
+ __ Movz(out_hi, a_hi, TMP);
+ __ Movz(out_lo, a_lo, TMP);
+ }
+ if (out_lo != b_lo) {
+ __ Movn(out_hi, b_hi, TMP);
+ __ Movn(out_lo, b_lo, TMP);
+ }
+ }
+ }
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimInt);
+ Register a = locations->InAt(0).AsRegister<Register>();
+ Register b = locations->InAt(1).AsRegister<Register>();
+ Register out = locations->Out().AsRegister<Register>();
+
+ if (a == b) {
+ if (out != a) {
+ __ Move(out, a);
+ }
+ } else {
+ __ Slt(AT, a, b);
+ if (is_min) {
+ if (out != a) {
+ __ Movn(out, a, AT);
+ }
+ if (out != b) {
+ __ Movz(out, b, AT);
+ }
+ } else {
+ if (out != a) {
+ __ Movz(out, a, AT);
+ }
+ if (out != b) {
+ __ Movn(out, b, AT);
+ }
+ }
+ }
+ }
+ }
+}
+
+// int java.lang.Math.min(int, int)
+void IntrinsicLocationsBuilderMIPS::VisitMathMinIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMinIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(),
+ /* is_min */ true,
+ Primitive::kPrimInt,
+ IsR6(),
+ GetAssembler());
+}
+
+// long java.lang.Math.min(long, long)
+void IntrinsicLocationsBuilderMIPS::VisitMathMinLongLong(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMinLongLong(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(),
+ /* is_min */ true,
+ Primitive::kPrimLong,
+ IsR6(),
+ GetAssembler());
+}
+
+// int java.lang.Math.max(int, int)
+void IntrinsicLocationsBuilderMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMaxIntInt(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(),
+ /* is_min */ false,
+ Primitive::kPrimInt,
+ IsR6(),
+ GetAssembler());
+}
+
+// long java.lang.Math.max(long, long)
+void IntrinsicLocationsBuilderMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
+ CreateIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathMaxLongLong(HInvoke* invoke) {
+ GenMinMax(invoke->GetLocations(),
+ /* is_min */ false,
+ Primitive::kPrimLong,
+ IsR6(),
+ GetAssembler());
+}
+
+// double java.lang.Math.sqrt(double)
+void IntrinsicLocationsBuilderMIPS::VisitMathSqrt(HInvoke* invoke) {
+ CreateFPToFPLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitMathSqrt(HInvoke* invoke) {
+ LocationSummary* locations = invoke->GetLocations();
+ MipsAssembler* assembler = GetAssembler();
+ FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister out = locations->Out().AsFpuRegister<FRegister>();
+
+ __ SqrtD(out, in);
+}
+
// byte libcore.io.Memory.peekByte(long address)
void IntrinsicLocationsBuilderMIPS::VisitMemoryPeekByte(HInvoke* invoke) {
CreateIntToIntLocations(arena_, invoke);
@@ -1151,19 +1709,6 @@ void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED)
UNIMPLEMENTED_INTRINSIC(IntegerBitCount)
UNIMPLEMENTED_INTRINSIC(LongBitCount)
-UNIMPLEMENTED_INTRINSIC(MathAbsDouble)
-UNIMPLEMENTED_INTRINSIC(MathAbsFloat)
-UNIMPLEMENTED_INTRINSIC(MathAbsInt)
-UNIMPLEMENTED_INTRINSIC(MathAbsLong)
-UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble)
-UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat)
-UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble)
-UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat)
-UNIMPLEMENTED_INTRINSIC(MathMinIntInt)
-UNIMPLEMENTED_INTRINSIC(MathMinLongLong)
-UNIMPLEMENTED_INTRINSIC(MathMaxIntInt)
-UNIMPLEMENTED_INTRINSIC(MathMaxLongLong)
-UNIMPLEMENTED_INTRINSIC(MathSqrt)
UNIMPLEMENTED_INTRINSIC(MathCeil)
UNIMPLEMENTED_INTRINSIC(MathFloor)
UNIMPLEMENTED_INTRINSIC(MathRint)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index cf3a3657de..bd4f5329da 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -87,7 +87,8 @@ static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS64* codegen) {
// restored!
class IntrinsicSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit IntrinsicSlowPathMIPS64(HInvoke* invoke) : invoke_(invoke) { }
+ explicit IntrinsicSlowPathMIPS64(HInvoke* invoke)
+ : SlowPathCodeMIPS64(invoke), invoke_(invoke) { }
void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE {
CodeGeneratorMIPS64* codegen = down_cast<CodeGeneratorMIPS64*>(codegen_in);
@@ -580,25 +581,71 @@ void IntrinsicCodeGeneratorMIPS64::VisitMathAbsLong(HInvoke* invoke) {
static void GenMinMaxFP(LocationSummary* locations,
bool is_min,
- bool is_double,
+ Primitive::Type type,
Mips64Assembler* assembler) {
- FpuRegister lhs = locations->InAt(0).AsFpuRegister<FpuRegister>();
- FpuRegister rhs = locations->InAt(1).AsFpuRegister<FpuRegister>();
+ FpuRegister a = locations->InAt(0).AsFpuRegister<FpuRegister>();
+ FpuRegister b = locations->InAt(1).AsFpuRegister<FpuRegister>();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
- if (is_double) {
+ Mips64Label noNaNs;
+ Mips64Label done;
+ FpuRegister ftmp = ((out != a) && (out != b)) ? out : FTMP;
+
+ // When Java computes min/max it prefers a NaN to a number; the
+ // behavior of MIPSR6 is to prefer numbers to NaNs, i.e., if one of
+ // the inputs is a NaN and the other is a valid number, the MIPS
+ // instruction will return the number; Java wants the NaN value
+ // returned. This is why there is extra logic preceding the use of
+ // the MIPS min.fmt/max.fmt instructions. If either a, or b holds a
+ // NaN, return the NaN, otherwise return the min/max.
+ if (type == Primitive::kPrimDouble) {
+ __ CmpUnD(FTMP, a, b);
+ __ Bc1eqz(FTMP, &noNaNs);
+
+ // One of the inputs is a NaN
+ __ CmpEqD(ftmp, a, a);
+ // If a == a then b is the NaN, otherwise a is the NaN.
+ __ SelD(ftmp, a, b);
+
+ if (ftmp != out) {
+ __ MovD(out, ftmp);
+ }
+
+ __ Bc(&done);
+
+ __ Bind(&noNaNs);
+
if (is_min) {
- __ MinD(out, lhs, rhs);
+ __ MinD(out, a, b);
} else {
- __ MaxD(out, lhs, rhs);
+ __ MaxD(out, a, b);
}
} else {
+ DCHECK_EQ(type, Primitive::kPrimFloat);
+ __ CmpUnS(FTMP, a, b);
+ __ Bc1eqz(FTMP, &noNaNs);
+
+ // One of the inputs is a NaN
+ __ CmpEqS(ftmp, a, a);
+ // If a == a then b is the NaN, otherwise a is the NaN.
+ __ SelS(ftmp, a, b);
+
+ if (ftmp != out) {
+ __ MovS(out, ftmp);
+ }
+
+ __ Bc(&done);
+
+ __ Bind(&noNaNs);
+
if (is_min) {
- __ MinS(out, lhs, rhs);
+ __ MinS(out, a, b);
} else {
- __ MaxS(out, lhs, rhs);
+ __ MaxS(out, a, b);
}
}
+
+ __ Bind(&done);
}
static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
@@ -616,7 +663,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke)
}
void IntrinsicCodeGeneratorMIPS64::VisitMathMinDoubleDouble(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ true, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimDouble, GetAssembler());
}
// float java.lang.Math.min(float, float)
@@ -625,7 +672,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorMIPS64::VisitMathMinFloatFloat(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, /* is_double */ false, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(), /* is_min */ true, Primitive::kPrimFloat, GetAssembler());
}
// double java.lang.Math.max(double, double)
@@ -634,7 +681,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke)
}
void IntrinsicCodeGeneratorMIPS64::VisitMathMaxDoubleDouble(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ true, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimDouble, GetAssembler());
}
// float java.lang.Math.max(float, float)
@@ -643,7 +690,7 @@ void IntrinsicLocationsBuilderMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) {
}
void IntrinsicCodeGeneratorMIPS64::VisitMathMaxFloatFloat(HInvoke* invoke) {
- GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, /* is_double */ false, GetAssembler());
+ GenMinMaxFP(invoke->GetLocations(), /* is_min */ false, Primitive::kPrimFloat, GetAssembler());
}
static void GenMinMax(LocationSummary* locations,
@@ -653,49 +700,55 @@ static void GenMinMax(LocationSummary* locations,
GpuRegister rhs = locations->InAt(1).AsRegister<GpuRegister>();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
- // Some architectures, such as ARM and MIPS (prior to r6), have a
- // conditional move instruction which only changes the target
- // (output) register if the condition is true (MIPS prior to r6 had
- // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
- // change the target (output) register. If the condition is true the
- // output register gets the contents of the "rs" register; otherwise,
- // the output register is set to zero. One consequence of this is
- // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
- // needs to use a pair of SELEQZ/SELNEZ instructions. After
- // executing this pair of instructions one of the output registers
- // from the pair will necessarily contain zero. Then the code ORs the
- // output registers from the SELEQZ/SELNEZ instructions to get the
- // final result.
- //
- // The initial test to see if the output register is same as the
- // first input register is needed to make sure that value in the
- // first input register isn't clobbered before we've finished
- // computing the output value. The logic in the corresponding else
- // clause performs the same task but makes sure the second input
- // register isn't clobbered in the event that it's the same register
- // as the output register; the else clause also handles the case
- // where the output register is distinct from both the first, and the
- // second input registers.
- if (out == lhs) {
- __ Slt(AT, rhs, lhs);
- if (is_min) {
- __ Seleqz(out, lhs, AT);
- __ Selnez(AT, rhs, AT);
- } else {
- __ Selnez(out, lhs, AT);
- __ Seleqz(AT, rhs, AT);
+ if (lhs == rhs) {
+ if (out != lhs) {
+ __ Move(out, lhs);
}
} else {
- __ Slt(AT, lhs, rhs);
- if (is_min) {
- __ Seleqz(out, rhs, AT);
- __ Selnez(AT, lhs, AT);
+ // Some architectures, such as ARM and MIPS (prior to r6), have a
+ // conditional move instruction which only changes the target
+ // (output) register if the condition is true (MIPS prior to r6 had
+ // MOVF, MOVT, and MOVZ). The SELEQZ and SELNEZ instructions always
+ // change the target (output) register. If the condition is true the
+ // output register gets the contents of the "rs" register; otherwise,
+ // the output register is set to zero. One consequence of this is
+ // that to implement something like "rd = c==0 ? rs : rt" MIPS64r6
+ // needs to use a pair of SELEQZ/SELNEZ instructions. After
+ // executing this pair of instructions one of the output registers
+ // from the pair will necessarily contain zero. Then the code ORs the
+ // output registers from the SELEQZ/SELNEZ instructions to get the
+ // final result.
+ //
+ // The initial test to see if the output register is same as the
+ // first input register is needed to make sure that value in the
+ // first input register isn't clobbered before we've finished
+ // computing the output value. The logic in the corresponding else
+ // clause performs the same task but makes sure the second input
+ // register isn't clobbered in the event that it's the same register
+ // as the output register; the else clause also handles the case
+ // where the output register is distinct from both the first, and the
+ // second input registers.
+ if (out == lhs) {
+ __ Slt(AT, rhs, lhs);
+ if (is_min) {
+ __ Seleqz(out, lhs, AT);
+ __ Selnez(AT, rhs, AT);
+ } else {
+ __ Selnez(out, lhs, AT);
+ __ Seleqz(AT, rhs, AT);
+ }
} else {
- __ Selnez(out, rhs, AT);
- __ Seleqz(AT, lhs, AT);
+ __ Slt(AT, lhs, rhs);
+ if (is_min) {
+ __ Seleqz(out, rhs, AT);
+ __ Selnez(AT, lhs, AT);
+ } else {
+ __ Selnez(out, rhs, AT);
+ __ Seleqz(AT, lhs, AT);
+ }
}
+ __ Or(out, out, AT);
}
- __ Or(out, out, AT);
}
static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h
index e70afd29f0..c1f9ae6425 100644
--- a/compiler/optimizing/intrinsics_utils.h
+++ b/compiler/optimizing/intrinsics_utils.h
@@ -39,7 +39,7 @@ namespace art {
template <typename TDexCallingConvention>
class IntrinsicSlowPath : public SlowPathCode {
public:
- explicit IntrinsicSlowPath(HInvoke* invoke) : invoke_(invoke) { }
+ explicit IntrinsicSlowPath(HInvoke* invoke) : SlowPathCode(invoke), invoke_(invoke) { }
Location MoveArguments(CodeGenerator* codegen) {
TDexCallingConvention calling_convention_visitor;
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index a6b4078f46..33bb2e8f30 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -141,6 +141,7 @@ void LICM::Run() {
DCHECK(!instruction->HasEnvironment());
}
instruction->MoveBefore(pre_header->GetLastInstruction());
+ MaybeRecordStat(MethodCompilationStat::kLoopInvariantMoved);
} else if (instruction->CanThrow()) {
// If `instruction` can throw, we cannot move further instructions
// that can throw as well.
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
index 0b5a0f103b..bf56f53d46 100644
--- a/compiler/optimizing/licm.h
+++ b/compiler/optimizing/licm.h
@@ -26,8 +26,9 @@ class SideEffectsAnalysis;
class LICM : public HOptimization {
public:
- LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
- : HOptimization(graph, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+ LICM(HGraph* graph, const SideEffectsAnalysis& side_effects, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kLoopInvariantCodeMotionPassName, stats),
+ side_effects_(side_effects) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 9fb32f4001..d446539700 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -79,7 +79,7 @@ class LICMTest : public CommonCompilerTest {
graph_->BuildDominatorTree();
SideEffectsAnalysis side_effects(graph_);
side_effects.Run();
- LICM(graph_, side_effects).Run();
+ LICM(graph_, side_effects, nullptr).Run();
}
// General building fields.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index b26ce0aa13..f9acb089ee 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1178,19 +1178,19 @@ HConstant* HUnaryOperation::TryStaticEvaluation() const {
}
HConstant* HBinaryOperation::TryStaticEvaluation() const {
- if (GetLeft()->IsIntConstant()) {
- if (GetRight()->IsIntConstant()) {
- return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsIntConstant());
- } else if (GetRight()->IsLongConstant()) {
- return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsLongConstant());
- }
+ if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) {
+ return Evaluate(GetLeft()->AsIntConstant(), GetRight()->AsIntConstant());
} else if (GetLeft()->IsLongConstant()) {
if (GetRight()->IsIntConstant()) {
+ // The binop(long, int) case is only valid for shifts and rotations.
+ DCHECK(IsShl() || IsShr() || IsUShr() || IsRor()) << DebugName();
return Evaluate(GetLeft()->AsLongConstant(), GetRight()->AsIntConstant());
} else if (GetRight()->IsLongConstant()) {
return Evaluate(GetLeft()->AsLongConstant(), GetRight()->AsLongConstant());
}
} else if (GetLeft()->IsNullConstant() && GetRight()->IsNullConstant()) {
+ // The binop(null, null) case is only valid for equal and not-equal conditions.
+ DCHECK(IsEqual() || IsNotEqual()) << DebugName();
return Evaluate(GetLeft()->AsNullConstant(), GetRight()->AsNullConstant());
} else if (kEnableFloatingPointStaticEvaluation) {
if (GetLeft()->IsFloatConstant() && GetRight()->IsFloatConstant()) {
@@ -1420,7 +1420,38 @@ HBasicBlock* HBasicBlock::SplitCatchBlockAfterMoveException() {
}
}
-HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+HBasicBlock* HBasicBlock::SplitBeforeForInlining(HInstruction* cursor) {
+ DCHECK_EQ(cursor->GetBlock(), this);
+
+ HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(),
+ cursor->GetDexPc());
+ new_block->instructions_.first_instruction_ = cursor;
+ new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+ instructions_.last_instruction_ = cursor->previous_;
+ if (cursor->previous_ == nullptr) {
+ instructions_.first_instruction_ = nullptr;
+ } else {
+ cursor->previous_->next_ = nullptr;
+ cursor->previous_ = nullptr;
+ }
+
+ new_block->instructions_.SetBlockOfInstructions(new_block);
+
+ for (HBasicBlock* successor : GetSuccessors()) {
+ new_block->successors_.push_back(successor);
+ successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block;
+ }
+ successors_.clear();
+
+ for (HBasicBlock* dominated : GetDominatedBlocks()) {
+ dominated->dominator_ = new_block;
+ new_block->dominated_blocks_.push_back(dominated);
+ }
+ dominated_blocks_.clear();
+ return new_block;
+}
+
+HBasicBlock* HBasicBlock::SplitAfterForInlining(HInstruction* cursor) {
DCHECK(!cursor->IsControlFlow());
DCHECK_NE(instructions_.last_instruction_, cursor);
DCHECK_EQ(cursor->GetBlock(), this);
@@ -1573,6 +1604,20 @@ void HInstructionList::AddAfter(HInstruction* cursor, const HInstructionList& in
}
}
+void HInstructionList::AddBefore(HInstruction* cursor, const HInstructionList& instruction_list) {
+ DCHECK(Contains(cursor));
+ if (!instruction_list.IsEmpty()) {
+ if (cursor == first_instruction_) {
+ first_instruction_ = instruction_list.first_instruction_;
+ } else {
+ cursor->previous_->next_ = instruction_list.first_instruction_;
+ }
+ instruction_list.last_instruction_->next_ = cursor;
+ instruction_list.first_instruction_->previous_ = cursor->previous_;
+ cursor->previous_ = instruction_list.last_instruction_;
+ }
+}
+
void HInstructionList::Add(const HInstructionList& instruction_list) {
if (IsEmpty()) {
first_instruction_ = instruction_list.first_instruction_;
@@ -1815,18 +1860,6 @@ void HBasicBlock::ReplaceWith(HBasicBlock* other) {
graph_ = nullptr;
}
-// Create space in `blocks` for adding `number_of_new_blocks` entries
-// starting at location `at`. Blocks after `at` are moved accordingly.
-static void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
- size_t number_of_new_blocks,
- size_t after) {
- DCHECK_LT(after, blocks->size());
- size_t old_size = blocks->size();
- size_t new_size = old_size + number_of_new_blocks;
- blocks->resize(new_size);
- std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
-}
-
void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) {
DCHECK_EQ(block->GetGraph(), this);
DCHECK(block->GetSuccessors().empty());
@@ -1880,7 +1913,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
DCHECK(!body->IsInLoop());
HInstruction* last = body->GetLastInstruction();
- invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+ // Note that we add instructions before the invoke only to simplify polymorphic inlining.
+ invoke->GetBlock()->instructions_.AddBefore(invoke, body->GetInstructions());
body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
// Replace the invoke with the return value of the inlined graph.
@@ -1898,7 +1932,8 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
// with the second half.
ArenaAllocator* allocator = outer_graph->GetArena();
HBasicBlock* at = invoke->GetBlock();
- HBasicBlock* to = at->SplitAfter(invoke);
+ // Note that we split before the invoke only to simplify polymorphic inlining.
+ HBasicBlock* to = at->SplitBeforeForInlining(invoke);
HBasicBlock* first = entry_block_->GetSuccessors()[0];
DCHECK(!first->IsInLoop());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 01ba704610..b355883a72 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -131,6 +131,7 @@ class HInstructionList : public ValueObject {
void SetBlockOfInstructions(HBasicBlock* block) const;
void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+ void AddBefore(HInstruction* cursor, const HInstructionList& instruction_list);
void Add(const HInstructionList& instruction_list);
// Return the number of instructions in the list. This is an expensive operation.
@@ -618,6 +619,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
friend class SsaBuilder; // For caching constants.
friend class SsaLivenessAnalysis; // For the linear order.
+ friend class HInliner; // For the reverse post order.
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
@@ -972,12 +974,15 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> {
// loop and try/catch information.
HBasicBlock* SplitBefore(HInstruction* cursor);
- // Split the block into two blocks just after `cursor`. Returns the newly
+ // Split the block into two blocks just before `cursor`. Returns the newly
// created block. Note that this method just updates raw block information,
// like predecessors, successors, dominators, and instruction list. It does not
// update the graph, reverse post order, loop information, nor make sure the
// blocks are consistent (for example ending with a control flow instruction).
- HBasicBlock* SplitAfter(HInstruction* cursor);
+ HBasicBlock* SplitBeforeForInlining(HInstruction* cursor);
+
+ // Similar to `SplitBeforeForInlining` but does it after `cursor`.
+ HBasicBlock* SplitAfterForInlining(HInstruction* cursor);
// Split catch block into two blocks after the original move-exception bytecode
// instruction, or at the beginning if not present. Returns the newly created,
@@ -2063,6 +2068,7 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
}
SideEffects GetSideEffects() const { return side_effects_; }
+ void SetSideEffects(SideEffects other) { side_effects_ = other; }
void AddSideEffects(SideEffects other) { side_effects_.Add(other); }
size_t GetLifetimePosition() const { return lifetime_position_; }
@@ -2101,7 +2107,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
- void SetSideEffects(SideEffects other) { side_effects_ = other; }
private:
void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
@@ -2816,20 +2821,15 @@ class HBinaryOperation : public HExpression<2> {
// Apply this operation to `x` and `y`.
virtual HConstant* Evaluate(HNullConstant* x ATTRIBUTE_UNUSED,
HNullConstant* y ATTRIBUTE_UNUSED) const {
- VLOG(compiler) << DebugName() << " is not defined for the (null, null) case.";
- return nullptr;
+ LOG(FATAL) << DebugName() << " is not defined for the (null, null) case.";
+ UNREACHABLE();
}
virtual HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const = 0;
virtual HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const = 0;
- virtual HConstant* Evaluate(HIntConstant* x ATTRIBUTE_UNUSED,
- HLongConstant* y ATTRIBUTE_UNUSED) const {
- VLOG(compiler) << DebugName() << " is not defined for the (int, long) case.";
- return nullptr;
- }
virtual HConstant* Evaluate(HLongConstant* x ATTRIBUTE_UNUSED,
HIntConstant* y ATTRIBUTE_UNUSED) const {
- VLOG(compiler) << DebugName() << " is not defined for the (long, int) case.";
- return nullptr;
+ LOG(FATAL) << DebugName() << " is not defined for the (long, int) case.";
+ UNREACHABLE();
}
virtual HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const = 0;
virtual HConstant* Evaluate(HDoubleConstant* x, HDoubleConstant* y) const = 0;
@@ -4300,8 +4300,6 @@ class HShl : public HBinaryOperation {
return GetBlock()->GetGraph()->GetIntConstant(
Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
}
- // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
- // case is handled as `x << static_cast<int>(y)`.
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
@@ -4346,8 +4344,6 @@ class HShr : public HBinaryOperation {
return GetBlock()->GetGraph()->GetIntConstant(
Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
}
- // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
- // case is handled as `x >> static_cast<int>(y)`.
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
@@ -4393,8 +4389,6 @@ class HUShr : public HBinaryOperation {
return GetBlock()->GetGraph()->GetIntConstant(
Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc());
}
- // There is no `Evaluate(HIntConstant* x, HLongConstant* y)`, as this
- // case is handled as `x >>> static_cast<int>(y)`.
HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc());
@@ -4430,21 +4424,12 @@ class HAnd : public HBinaryOperation {
bool IsCommutative() const OVERRIDE { return true; }
- template <typename T, typename U>
- auto Compute(T x, U y) const -> decltype(x & y) { return x & y; }
+ template <typename T> T Compute(T x, T y) const { return x & y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
- HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
- }
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
- }
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -4476,21 +4461,12 @@ class HOr : public HBinaryOperation {
bool IsCommutative() const OVERRIDE { return true; }
- template <typename T, typename U>
- auto Compute(T x, U y) const -> decltype(x | y) { return x | y; }
+ template <typename T> T Compute(T x, T y) const { return x | y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
- HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
- }
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
- }
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -4522,21 +4498,12 @@ class HXor : public HBinaryOperation {
bool IsCommutative() const OVERRIDE { return true; }
- template <typename T, typename U>
- auto Compute(T x, U y) const -> decltype(x ^ y) { return x ^ y; }
+ template <typename T> T Compute(T x, T y) const { return x ^ y; }
HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetIntConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
}
- HConstant* Evaluate(HIntConstant* x, HLongConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
- }
- HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE {
- return GetBlock()->GetGraph()->GetLongConstant(
- Compute(x->GetValue(), y->GetValue()), GetDexPc());
- }
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
return GetBlock()->GetGraph()->GetLongConstant(
Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -6370,6 +6337,18 @@ class SwitchTable : public ValueObject {
DISALLOW_COPY_AND_ASSIGN(SwitchTable);
};
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+inline void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
+ size_t number_of_new_blocks,
+ size_t after) {
+ DCHECK_LT(after, blocks->size());
+ size_t old_size = blocks->size();
+ size_t new_size = old_size + number_of_new_blocks;
+ blocks->resize(new_size);
+ std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
+}
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 12b748b7b6..b1891c979e 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -505,12 +505,12 @@ static void RunOptimizations(HGraph* graph,
graph, stats, HDeadCodeElimination::kFinalDeadCodeEliminationPassName);
HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
- HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph);
+ HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph, stats);
HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding_after_bce");
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects);
- LICM* licm = new (arena) LICM(graph, *side_effects);
+ LICM* licm = new (arena) LICM(graph, *side_effects, stats);
LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects);
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction);
@@ -519,7 +519,7 @@ static void RunOptimizations(HGraph* graph,
graph, stats, "instruction_simplifier_after_bce");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_before_codegen");
- IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver);
+ IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver, stats);
HOptimization* optimizations1[] = {
intrinsics,
@@ -651,7 +651,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
DexCompilationUnit dex_compilation_unit(
nullptr, class_loader, Runtime::Current()->GetClassLinker(), dex_file, code_item,
class_def_idx, method_idx, access_flags,
- compiler_driver->GetVerifiedMethod(&dex_file, method_idx), dex_cache);
+ nullptr, dex_cache);
bool requires_barrier = dex_compilation_unit.IsConstructor()
&& compiler_driver->RequiresConstructorBarrier(Thread::Current(),
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 52a7b10cad..179004bd40 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -56,6 +56,10 @@ enum MethodCompilationStat {
kMonomorphicCall,
kPolymorphicCall,
kMegamorphicCall,
+ kBooleanSimplified,
+ kIntrinsicRecognized,
+ kLoopInvariantMoved,
+ kSelectGenerated,
kLastStat
};
@@ -124,7 +128,11 @@ class OptimizingCompilerStats {
case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break;
case kMonomorphicCall: name = "MonomorphicCall"; break;
case kPolymorphicCall: name = "PolymorphicCall"; break;
- case kMegamorphicCall: name = "kMegamorphicCall"; break;
+ case kMegamorphicCall: name = "MegamorphicCall"; break;
+ case kBooleanSimplified : name = "BooleanSimplified"; break;
+ case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
+ case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
+ case kSelectGenerated : name = "SelectGenerated"; break;
case kLastStat:
LOG(FATAL) << "invalid stat "
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 105b30ae5d..e52476ea03 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -141,6 +141,8 @@ void HSelectGenerator::Run() {
block->MergeWith(merge_block);
}
+ MaybeRecordStat(MethodCompilationStat::kSelectGenerated);
+
// No need to update dominance information, as we are simplifying
// a simple diamond shape, where the join block is merged with the
// entry block. Any following blocks would have had the join block
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index f9d6d4d8de..c6dca581cc 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -47,8 +47,8 @@ namespace art {
class HSelectGenerator : public HOptimization {
public:
- explicit HSelectGenerator(HGraph* graph)
- : HOptimization(graph, kSelectGeneratorPassName) {}
+ HSelectGenerator(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kSelectGeneratorPassName, stats) {}
void Run() OVERRIDE;
diff --git a/compiler/profile_assistant.h b/compiler/profile_assistant.h
deleted file mode 100644
index ad5e2163cf..0000000000
--- a/compiler/profile_assistant.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_PROFILE_ASSISTANT_H_
-#define ART_COMPILER_PROFILE_ASSISTANT_H_
-
-#include <string>
-#include <vector>
-
-#include "base/scoped_flock.h"
-#include "jit/offline_profiling_info.cc"
-
-namespace art {
-
-class ProfileAssistant {
- public:
- // Process the profile information present in the given files. Returns true
- // if the analysis ended up successfully (i.e. no errors during reading,
- // merging or writing of profile files).
- //
- // If the returned value is true and there is a significant difference between
- // profile_files and reference_profile_files:
- // - profile_compilation_info is set to a not null object that
- // can be used to drive compilation. It will be the merge of all the data
- // found in profile_files and reference_profile_files.
- // - the data from profile_files[i] is merged into
- // reference_profile_files[i] and the corresponding backing file is
- // updated.
- //
- // If the returned value is false or the difference is insignificant,
- // profile_compilation_info will be set to null.
- //
- // Additional notes:
- // - as mentioned above, this function may update the content of the files
- // passed with the reference_profile_files.
- // - if reference_profile_files is not empty it must be the same size as
- // profile_files.
- static bool ProcessProfiles(
- const std::vector<std::string>& profile_files,
- const std::vector<std::string>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info);
-
- static bool ProcessProfiles(
- const std::vector<uint32_t>& profile_files_fd_,
- const std::vector<uint32_t>& reference_profile_files_fd_,
- /*out*/ ProfileCompilationInfo** profile_compilation_info);
-
- private:
- static bool ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const std::vector<ScopedFlock>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info);
-
- DISALLOW_COPY_AND_ASSIGN(ProfileAssistant);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_PROFILE_ASSISTANT_H_
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index f96376d9fe..a894565425 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -545,6 +545,9 @@ class ArmAssembler : public Assembler {
virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0;
virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0;
virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0;
+ virtual void rev(Register rd, Register rm, Condition cond = AL) = 0;
+ virtual void rev16(Register rd, Register rm, Condition cond = AL) = 0;
+ virtual void revsh(Register rd, Register rm, Condition cond = AL) = 0;
// Multiply instructions.
virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index ebca25bbf9..0a227b21cd 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -750,6 +750,35 @@ void Arm32Assembler::movt(Register rd, uint16_t imm16, Condition cond) {
}
+void Arm32Assembler::EmitMiscellaneous(Condition cond, uint8_t op1,
+ uint8_t op2, uint32_t a_part,
+ uint32_t rest) {
+ int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
+ B26 | B25 | B23 |
+ (op1 << 20) |
+ (a_part << 16) |
+ (op2 << 5) |
+ B4 |
+ rest;
+ Emit(encoding);
+}
+
+
+void Arm32Assembler::EmitReverseBytes(Register rd, Register rm, Condition cond,
+ uint8_t op1, uint8_t op2) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rm, kNoRegister);
+ CHECK_NE(cond, kNoCondition);
+ CHECK_NE(rd, PC);
+ CHECK_NE(rm, PC);
+
+ int32_t encoding = (static_cast<int32_t>(rd) << kRdShift) |
+ (0b1111 << 8) |
+ static_cast<int32_t>(rm);
+ EmitMiscellaneous(cond, op1, op2, 0b1111, encoding);
+}
+
+
void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) {
CHECK_NE(rd, kNoRegister);
CHECK_NE(rm, kNoRegister);
@@ -764,6 +793,21 @@ void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) {
}
+void Arm32Assembler::rev(Register rd, Register rm, Condition cond) {
+ EmitReverseBytes(rd, rm, cond, 0b011, 0b001);
+}
+
+
+void Arm32Assembler::rev16(Register rd, Register rm, Condition cond) {
+ EmitReverseBytes(rd, rm, cond, 0b011, 0b101);
+}
+
+
+void Arm32Assembler::revsh(Register rd, Register rm, Condition cond) {
+ EmitReverseBytes(rd, rm, cond, 0b111, 0b101);
+}
+
+
void Arm32Assembler::EmitMulOp(Condition cond, int32_t opcode,
Register rd, Register rn,
Register rm, Register rs) {
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index bf332feb62..e3e05caf92 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -91,6 +91,9 @@ class Arm32Assembler FINAL : public ArmAssembler {
void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
+ void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE;
+ void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE;
+ void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE;
// Multiply instructions.
void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
@@ -388,6 +391,11 @@ class Arm32Assembler FINAL : public ArmAssembler {
void EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond);
+ void EmitMiscellaneous(Condition cond, uint8_t op1, uint8_t op2,
+ uint32_t a_part, uint32_t rest);
+ void EmitReverseBytes(Register rd, Register rm, Condition cond,
+ uint8_t op1, uint8_t op2);
+
void EmitBranch(Condition cond, Label* label, bool link);
static int32_t EncodeBranchOffset(int offset, int32_t inst);
static int DecodeBranchOffset(int32_t inst);
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 43805966a9..e570e22fca 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -887,4 +887,16 @@ TEST_F(AssemblerArm32Test, rbit) {
T3Helper(&arm::Arm32Assembler::rbit, true, "rbit{cond} {reg1}, {reg2}", "rbit");
}
+TEST_F(AssemblerArm32Test, rev) {
+ T3Helper(&arm::Arm32Assembler::rev, true, "rev{cond} {reg1}, {reg2}", "rev");
+}
+
+TEST_F(AssemblerArm32Test, rev16) {
+ T3Helper(&arm::Arm32Assembler::rev16, true, "rev16{cond} {reg1}, {reg2}", "rev16");
+}
+
+TEST_F(AssemblerArm32Test, revsh) {
+ T3Helper(&arm::Arm32Assembler::revsh, true, "revsh{cond} {reg1}, {reg2}", "revsh");
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 52023a67ee..15298b390b 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2569,20 +2569,36 @@ void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x
}
+void Thumb2Assembler::Emit32Miscellaneous(uint8_t op1,
+ uint8_t op2,
+ uint32_t rest_encoding) {
+ int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B23 |
+ op1 << 20 |
+ 0xf << 12 |
+ B7 |
+ op2 << 4 |
+ rest_encoding;
+ Emit32(encoding);
+}
+
+
+void Thumb2Assembler::Emit16Miscellaneous(uint32_t rest_encoding) {
+ int16_t encoding = B15 | B13 | B12 |
+ rest_encoding;
+ Emit16(encoding);
+}
+
void Thumb2Assembler::clz(Register rd, Register rm, Condition cond) {
CHECK_NE(rd, kNoRegister);
CHECK_NE(rm, kNoRegister);
CheckCondition(cond);
CHECK_NE(rd, PC);
CHECK_NE(rm, PC);
- int32_t encoding = B31 | B30 | B29 | B28 | B27 |
- B25 | B23 | B21 | B20 |
+ int32_t encoding =
static_cast<uint32_t>(rm) << 16 |
- 0xf << 12 |
static_cast<uint32_t>(rd) << 8 |
- B7 |
static_cast<uint32_t>(rm);
- Emit32(encoding);
+ Emit32Miscellaneous(0b11, 0b00, encoding);
}
@@ -2630,14 +2646,55 @@ void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) {
CHECK_NE(rm, PC);
CHECK_NE(rd, SP);
CHECK_NE(rm, SP);
- int32_t encoding = B31 | B30 | B29 | B28 | B27 |
- B25 | B23 | B20 |
+ int32_t encoding =
static_cast<uint32_t>(rm) << 16 |
- 0xf << 12 |
static_cast<uint32_t>(rd) << 8 |
- B7 | B5 |
static_cast<uint32_t>(rm);
- Emit32(encoding);
+
+ Emit32Miscellaneous(0b01, 0b10, encoding);
+}
+
+
+void Thumb2Assembler::EmitReverseBytes(Register rd, Register rm,
+ uint32_t op) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rm, kNoRegister);
+ CHECK_NE(rd, PC);
+ CHECK_NE(rm, PC);
+ CHECK_NE(rd, SP);
+ CHECK_NE(rm, SP);
+
+ if (!IsHighRegister(rd) && !IsHighRegister(rm) && !force_32bit_) {
+ uint16_t t1_op = B11 | B9 | (op << 6);
+ int16_t encoding = t1_op |
+ static_cast<uint16_t>(rm) << 3 |
+ static_cast<uint16_t>(rd);
+ Emit16Miscellaneous(encoding);
+ } else {
+ int32_t encoding =
+ static_cast<uint32_t>(rm) << 16 |
+ static_cast<uint32_t>(rd) << 8 |
+ static_cast<uint32_t>(rm);
+ Emit32Miscellaneous(0b01, op, encoding);
+ }
+}
+
+
+void Thumb2Assembler::rev(Register rd, Register rm, Condition cond) {
+ CheckCondition(cond);
+ EmitReverseBytes(rd, rm, 0b00);
+}
+
+
+void Thumb2Assembler::rev16(Register rd, Register rm, Condition cond) {
+ CheckCondition(cond);
+ EmitReverseBytes(rd, rm, 0b01);
+}
+
+
+void Thumb2Assembler::revsh(Register rd, Register rm, Condition cond) {
+ CheckCondition(cond);
+ EmitReverseBytes(rd, rm, 0b11);
}
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index bf07b2dbf8..6b61acafac 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -117,6 +117,9 @@ class Thumb2Assembler FINAL : public ArmAssembler {
void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
+ void rev(Register rd, Register rm, Condition cond = AL) OVERRIDE;
+ void rev16(Register rd, Register rm, Condition cond = AL) OVERRIDE;
+ void revsh(Register rd, Register rm, Condition cond = AL) OVERRIDE;
// Multiply instructions.
void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
@@ -644,6 +647,17 @@ class Thumb2Assembler FINAL : public ArmAssembler {
Register rd,
const ShifterOperand& so);
+ // Emit a single 32 bit miscellaneous instruction.
+ void Emit32Miscellaneous(uint8_t op1,
+ uint8_t op2,
+ uint32_t rest_encoding);
+
+ // Emit reverse byte instructions: rev, rev16, revsh.
+ void EmitReverseBytes(Register rd, Register rm, uint32_t op);
+
+ // Emit a single 16 bit miscellaneous instruction.
+ void Emit16Miscellaneous(uint32_t rest_encoding);
+
// Must the instruction be 32 bits or can it possibly be encoded
// in 16 bits?
bool Is32BitDataProcessing(Condition cond,
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 7b32b0fd26..650b08900b 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1331,4 +1331,28 @@ TEST_F(AssemblerThumb2Test, rbit) {
DriverStr(expected, "rbit");
}
+TEST_F(AssemblerThumb2Test, rev) {
+ __ rev(arm::R1, arm::R0);
+
+ const char* expected = "rev r1, r0\n";
+
+ DriverStr(expected, "rev");
+}
+
+TEST_F(AssemblerThumb2Test, rev16) {
+ __ rev16(arm::R1, arm::R0);
+
+ const char* expected = "rev16 r1, r0\n";
+
+ DriverStr(expected, "rev16");
+}
+
+TEST_F(AssemblerThumb2Test, revsh) {
+ __ revsh(arm::R1, arm::R0);
+
+ const char* expected = "revsh r1, r0\n";
+
+ DriverStr(expected, "revsh");
+}
+
} // namespace art
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 6fd65ee9a4..7c41813457 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -537,12 +537,20 @@ void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
EmitI(0x7, rt, static_cast<Register>(0), imm16);
}
+void MipsAssembler::Bc1f(uint16_t imm16) {
+ Bc1f(0, imm16);
+}
+
void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16);
}
+void MipsAssembler::Bc1t(uint16_t imm16) {
+ Bc1t(0, imm16);
+}
+
void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
@@ -843,6 +851,22 @@ void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
EmitFR(0x11, 0x11, ft, fs, fd, 0x3);
}
+void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
+ EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4);
+}
+
+void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
+ EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4);
+}
+
+void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
+ EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5);
+}
+
+void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
+ EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5);
+}
+
void MipsAssembler::MovS(FRegister fd, FRegister fs) {
EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6);
}
@@ -859,84 +883,140 @@ void MipsAssembler::NegD(FRegister fd, FRegister fs) {
EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7);
}
+void MipsAssembler::CunS(FRegister fs, FRegister ft) {
+ CunS(0, fs, ft);
+}
+
void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
}
+void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
+ CeqS(0, fs, ft);
+}
+
void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
}
+void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
+ CueqS(0, fs, ft);
+}
+
void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
}
+void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
+ ColtS(0, fs, ft);
+}
+
void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
}
+void MipsAssembler::CultS(FRegister fs, FRegister ft) {
+ CultS(0, fs, ft);
+}
+
void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
}
+void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
+ ColeS(0, fs, ft);
+}
+
void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
}
+void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
+ CuleS(0, fs, ft);
+}
+
void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
}
+void MipsAssembler::CunD(FRegister fs, FRegister ft) {
+ CunD(0, fs, ft);
+}
+
void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
}
+void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
+ CeqD(0, fs, ft);
+}
+
void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
}
+void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
+ CueqD(0, fs, ft);
+}
+
void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
}
+void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
+ ColtD(0, fs, ft);
+}
+
void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
}
+void MipsAssembler::CultD(FRegister fs, FRegister ft) {
+ CultD(0, fs, ft);
+}
+
void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
}
+void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
+ ColeD(0, fs, ft);
+}
+
void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
}
+void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
+ CuleD(0, fs, ft);
+}
+
void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
CHECK(!IsR6());
CHECK(IsUint<3>(cc)) << cc;
@@ -1055,6 +1135,70 @@ void MipsAssembler::Movt(Register rd, Register rs, int cc) {
EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01);
}
+void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11);
+}
+
+void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11);
+}
+
+void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11);
+}
+
+void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
+ CHECK(!IsR6());
+ CHECK(IsUint<3>(cc)) << cc;
+ EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11);
+}
+
+void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x10);
+}
+
+void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x11, ft, fs, fd, 0x10);
+}
+
+void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b);
+}
+
+void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b);
+}
+
+void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x1c);
+}
+
+void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x11, ft, fs, fd, 0x1c);
+}
+
+void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x10, ft, fs, fd, 0x1e);
+}
+
+void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
+ CHECK(IsR6());
+ EmitFR(0x11, 0x11, ft, fs, fd, 0x1e);
+}
+
void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09);
}
@@ -1095,6 +1239,14 @@ void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21);
}
+void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
+ EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf);
+}
+
+void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
+ EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf);
+}
+
void MipsAssembler::Mfc1(Register rt, FRegister fs) {
EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
}
@@ -2062,11 +2214,19 @@ void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
}
}
+void MipsAssembler::Bc1f(MipsLabel* label) {
+ Bc1f(0, label);
+}
+
void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
CHECK(IsUint<3>(cc)) << cc;
Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
}
+void MipsAssembler::Bc1t(MipsLabel* label) {
+ Bc1t(0, label);
+}
+
void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
CHECK(IsUint<3>(cc)) << cc;
Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index 2262af49b3..a7179fd1dc 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -51,6 +51,20 @@ enum StoreOperandType {
kStoreDoubleword
};
+// Used to test the values returned by ClassS/ClassD.
+enum FPClassMaskType {
+ kSignalingNaN = 0x001,
+ kQuietNaN = 0x002,
+ kNegativeInfinity = 0x004,
+ kNegativeNormal = 0x008,
+ kNegativeSubnormal = 0x010,
+ kNegativeZero = 0x020,
+ kPositiveInfinity = 0x040,
+ kPositiveNormal = 0x080,
+ kPositiveSubnormal = 0x100,
+ kPositiveZero = 0x200,
+};
+
class MipsLabel : public Label {
public:
MipsLabel() : prev_branch_id_plus_one_(0) {}
@@ -191,7 +205,9 @@ class MipsAssembler FINAL : public Assembler {
void Bgez(Register rt, uint16_t imm16);
void Blez(Register rt, uint16_t imm16);
void Bgtz(Register rt, uint16_t imm16);
+ void Bc1f(uint16_t imm16); // R2
void Bc1f(int cc, uint16_t imm16); // R2
+ void Bc1t(uint16_t imm16); // R2
void Bc1t(int cc, uint16_t imm16); // R2
void J(uint32_t addr26);
void Jal(uint32_t addr26);
@@ -227,24 +243,42 @@ class MipsAssembler FINAL : public Assembler {
void SubD(FRegister fd, FRegister fs, FRegister ft);
void MulD(FRegister fd, FRegister fs, FRegister ft);
void DivD(FRegister fd, FRegister fs, FRegister ft);
+ void SqrtS(FRegister fd, FRegister fs);
+ void SqrtD(FRegister fd, FRegister fs);
+ void AbsS(FRegister fd, FRegister fs);
+ void AbsD(FRegister fd, FRegister fs);
void MovS(FRegister fd, FRegister fs);
void MovD(FRegister fd, FRegister fs);
void NegS(FRegister fd, FRegister fs);
void NegD(FRegister fd, FRegister fs);
+ void CunS(FRegister fs, FRegister ft); // R2
void CunS(int cc, FRegister fs, FRegister ft); // R2
+ void CeqS(FRegister fs, FRegister ft); // R2
void CeqS(int cc, FRegister fs, FRegister ft); // R2
+ void CueqS(FRegister fs, FRegister ft); // R2
void CueqS(int cc, FRegister fs, FRegister ft); // R2
+ void ColtS(FRegister fs, FRegister ft); // R2
void ColtS(int cc, FRegister fs, FRegister ft); // R2
+ void CultS(FRegister fs, FRegister ft); // R2
void CultS(int cc, FRegister fs, FRegister ft); // R2
+ void ColeS(FRegister fs, FRegister ft); // R2
void ColeS(int cc, FRegister fs, FRegister ft); // R2
+ void CuleS(FRegister fs, FRegister ft); // R2
void CuleS(int cc, FRegister fs, FRegister ft); // R2
+ void CunD(FRegister fs, FRegister ft); // R2
void CunD(int cc, FRegister fs, FRegister ft); // R2
+ void CeqD(FRegister fs, FRegister ft); // R2
void CeqD(int cc, FRegister fs, FRegister ft); // R2
+ void CueqD(FRegister fs, FRegister ft); // R2
void CueqD(int cc, FRegister fs, FRegister ft); // R2
+ void ColtD(FRegister fs, FRegister ft); // R2
void ColtD(int cc, FRegister fs, FRegister ft); // R2
+ void CultD(FRegister fs, FRegister ft); // R2
void CultD(int cc, FRegister fs, FRegister ft); // R2
+ void ColeD(FRegister fs, FRegister ft); // R2
void ColeD(int cc, FRegister fs, FRegister ft); // R2
+ void CuleD(FRegister fs, FRegister ft); // R2
void CuleD(int cc, FRegister fs, FRegister ft); // R2
void CmpUnS(FRegister fd, FRegister fs, FRegister ft); // R6
void CmpEqS(FRegister fd, FRegister fs, FRegister ft); // R6
@@ -266,8 +300,20 @@ class MipsAssembler FINAL : public Assembler {
void CmpOrD(FRegister fd, FRegister fs, FRegister ft); // R6
void CmpUneD(FRegister fd, FRegister fs, FRegister ft); // R6
void CmpNeD(FRegister fd, FRegister fs, FRegister ft); // R6
- void Movf(Register rd, Register rs, int cc); // R2
- void Movt(Register rd, Register rs, int cc); // R2
+ void Movf(Register rd, Register rs, int cc = 0); // R2
+ void Movt(Register rd, Register rs, int cc = 0); // R2
+ void MovfS(FRegister fd, FRegister fs, int cc = 0); // R2
+ void MovfD(FRegister fd, FRegister fs, int cc = 0); // R2
+ void MovtS(FRegister fd, FRegister fs, int cc = 0); // R2
+ void MovtD(FRegister fd, FRegister fs, int cc = 0); // R2
+ void SelS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void SelD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void ClassS(FRegister fd, FRegister fs); // R6
+ void ClassD(FRegister fd, FRegister fs); // R6
+ void MinS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void MinD(FRegister fd, FRegister fs, FRegister ft); // R6
+ void MaxS(FRegister fd, FRegister fs, FRegister ft); // R6
+ void MaxD(FRegister fd, FRegister fs, FRegister ft); // R6
void TruncLS(FRegister fd, FRegister fs); // R2+, FR=1
void TruncLD(FRegister fd, FRegister fs); // R2+, FR=1
@@ -279,6 +325,8 @@ class MipsAssembler FINAL : public Assembler {
void Cvtds(FRegister fd, FRegister fs);
void Cvtsl(FRegister fd, FRegister fs); // R2+, FR=1
void Cvtdl(FRegister fd, FRegister fs); // R2+, FR=1
+ void FloorWS(FRegister fd, FRegister fs);
+ void FloorWD(FRegister fd, FRegister fs);
void Mfc1(Register rt, FRegister fs);
void Mtc1(Register rt, FRegister fs);
@@ -322,7 +370,9 @@ class MipsAssembler FINAL : public Assembler {
void Bge(Register rs, Register rt, MipsLabel* label);
void Bltu(Register rs, Register rt, MipsLabel* label);
void Bgeu(Register rs, Register rt, MipsLabel* label);
+ void Bc1f(MipsLabel* label); // R2
void Bc1f(int cc, MipsLabel* label); // R2
+ void Bc1t(MipsLabel* label); // R2
void Bc1t(int cc, MipsLabel* label); // R2
void Bc1eqz(FRegister ft, MipsLabel* label); // R6
void Bc1nez(FRegister ft, MipsLabel* label); // R6
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 77f8d6cd9f..dfc379fdae 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -55,20 +55,42 @@ ifeq ($(ART_BUILD_TARGET_DEBUG),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain,art/compiler,target,debug,$(dex2oat_target_arch)))
endif
+# Note: the order is important because of static linking resolution.
+DEX2OAT_STATIC_DEPENDENCIES := \
+ libziparchive-host \
+ libnativehelper \
+ libnativebridge \
+ libnativeloader \
+ libsigchain_dummy \
+ libvixl \
+ liblog \
+ libz \
+ libbacktrace \
+ libLLVMObject \
+ libLLVMBitReader \
+ libLLVMMC \
+ libLLVMMCParser \
+ libLLVMCore \
+ libLLVMSupport \
+ libcutils \
+ libunwindbacktrace \
+ libutils \
+ libbase \
+ liblz4 \
+ liblzma
+
# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
ifeq ($(ART_BUILD_HOST_NDEBUG),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive-host liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch)))
ifeq ($(ART_BUILD_HOST_STATIC),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixl liblog libz \
- libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch),static))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static))
endif
endif
ifeq ($(ART_BUILD_HOST_DEBUG),true)
$(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive-host liblz4,art/compiler,host,debug,$(dex2oat_host_arch)))
ifeq ($(ART_BUILD_HOST_STATIC),true)
- $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libziparchive-host libnativehelper libnativebridge libsigchain_dummy libvixld liblog libz \
- libbacktrace libLLVMObject libLLVMBitReader libLLVMMC libLLVMMCParser libLLVMCore libLLVMSupport libcutils libunwindbacktrace libutils libbase liblz4,art/compiler,host,debug,$(dex2oat_host_arch),static))
+ $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
endif
endif
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 541fb5a423..cac12d1920 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -40,6 +40,7 @@
#include "art_method-inl.h"
#include "base/dumpable.h"
#include "base/macros.h"
+#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/stringpiece.h"
#include "base/time_utils.h"
@@ -65,13 +66,13 @@
#include "interpreter/unstarted_runtime.h"
#include "jit/offline_profiling_info.h"
#include "leb128.h"
+#include "linker/multi_oat_relative_patcher.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "oat_writer.h"
#include "os.h"
-#include "profile_assistant.h"
#include "runtime.h"
#include "runtime_options.h"
#include "ScopedLocalRef.h"
@@ -339,23 +340,10 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" Example: --runtime-arg -Xms256m");
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
- UsageError(" Can be specified multiple time, in which case the data from the different");
- UsageError(" profiles will be aggregated.");
- UsageError("");
- UsageError(" --reference-profile-file=<filename>: specify a reference profile file to use when");
- UsageError(" compiling. The data in this file will be compared with the data in the");
- UsageError(" associated --profile-file and the compilation will proceed only if there is");
- UsageError(" a significant difference (--reference-profile-file is paired with");
- UsageError(" --profile-file in the natural order). If the compilation was attempted then");
- UsageError(" --profile-file will be merged into --reference-profile-file. Valid only when");
- UsageError(" specified together with --profile-file.");
UsageError("");
UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
UsageError(" Cannot be used together with --profile-file.");
UsageError("");
- UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
- UsageError(" accepts a file descriptor. Cannot be used together with");
- UsageError(" --reference-profile-file.");
UsageError(" --print-pass-names: print a list of pass names");
UsageError("");
UsageError(" --disable-passes=<pass-names>: disable one or more passes separated by comma.");
@@ -517,14 +505,6 @@ static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
}
-static void CloseAllFds(const std::vector<uint32_t>& fds, const char* descriptor) {
- for (size_t i = 0; i < fds.size(); i++) {
- if (close(fds[i]) < 0) {
- PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i;
- }
- }
-}
-
class Dex2Oat FINAL {
public:
explicit Dex2Oat(TimingLogger* timings) :
@@ -567,10 +547,12 @@ class Dex2Oat FINAL {
dump_passes_(false),
dump_timing_(false),
dump_slow_timing_(kIsDebugBuild),
- swap_fd_(-1),
+ swap_fd_(kInvalidFd),
app_image_fd_(kInvalidFd),
+ profile_file_fd_(kInvalidFd),
timings_(timings),
- force_determinism_(false) {}
+ force_determinism_(false)
+ {}
~Dex2Oat() {
// Log completion time before deleting the runtime_, because this accesses
@@ -824,25 +806,8 @@ class Dex2Oat FINAL {
}
}
- if (!profile_files_.empty() && !profile_files_fd_.empty()) {
- Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
- }
- if (!profile_files_.empty()) {
- if (!reference_profile_files_.empty() &&
- (reference_profile_files_.size() != profile_files_.size())) {
- Usage("If specified, --reference-profile-file should match the number of --profile-file.");
- }
- } else if (!reference_profile_files_.empty()) {
- Usage("--reference-profile-file should only be supplied with --profile-file");
- }
- if (!profile_files_fd_.empty()) {
- if (!reference_profile_files_fd_.empty() &&
- (reference_profile_files_fd_.size() != profile_files_fd_.size())) {
- Usage("If specified, --reference-profile-file-fd should match the number",
- " of --profile-file-fd.");
- }
- } else if (!reference_profile_files_fd_.empty()) {
- Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd");
+ if (!profile_file_.empty() && (profile_file_fd_ != kInvalidFd)) {
+ Usage("Profile file should not be specified with both --profile-file-fd and --profile-file");
}
if (!parser_options->oat_symbols.empty()) {
@@ -1153,16 +1118,9 @@ class Dex2Oat FINAL {
} else if (option.starts_with("--compiler-backend=")) {
ParseCompilerBackend(option, parser_options.get());
} else if (option.starts_with("--profile-file=")) {
- profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
- } else if (option.starts_with("--reference-profile-file=")) {
- reference_profile_files_.push_back(
- option.substr(strlen("--reference-profile-file=")).ToString());
+ profile_file_ = option.substr(strlen("--profile-file=")).ToString();
} else if (option.starts_with("--profile-file-fd=")) {
- ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
- } else if (option.starts_with("--reference-profile-file-fd=")) {
- ParseFdForCollection(option, "--reference_profile-file-fd", &reference_profile_files_fd_);
- } else if (option == "--no-profile-file") {
- // No profile
+ ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage);
} else if (option == "--host") {
is_host_ = true;
} else if (option == "--runtime-arg") {
@@ -1408,7 +1366,7 @@ class Dex2Oat FINAL {
if (opened_dex_files_map != nullptr) {
opened_dex_files_maps_.push_back(std::move(opened_dex_files_map));
for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
- dex_file_oat_filename_map_.emplace(dex_file.get(), oat_filenames_[i]);
+ dex_file_oat_index_map_.emplace(dex_file.get(), i);
opened_dex_files_.push_back(std::move(dex_file));
}
} else {
@@ -1557,13 +1515,12 @@ class Dex2Oat FINAL {
IsBootImage(),
image_classes_.release(),
compiled_classes_.release(),
- nullptr,
+ /* compiled_methods */ nullptr,
thread_count_,
dump_stats_,
dump_passes_,
compiler_phases_timings_.get(),
swap_fd_,
- &dex_file_oat_filename_map_,
profile_compilation_info_.get()));
driver_->SetDexFilesForOatFile(dex_files_);
driver_->CompileAll(class_loader_, dex_files_, timings_);
@@ -1667,7 +1624,7 @@ class Dex2Oat FINAL {
IsAppImage(),
image_storage_mode_,
oat_filenames_,
- dex_file_oat_filename_map_));
+ dex_file_oat_index_map_));
// We need to prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -1677,21 +1634,39 @@ class Dex2Oat FINAL {
}
}
+ linker::MultiOatRelativePatcher patcher(instruction_set_, instruction_set_features_.get());
{
TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_);
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
- std::unique_ptr<File>& oat_file = oat_files_[i];
std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i];
std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i];
- oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files);
+ oat_writer->PrepareLayout(driver_.get(), image_writer_.get(), dex_files, &patcher);
- // We need to mirror the layout of the ELF file in the compressed debug-info.
- // Therefore we need to propagate the sizes of at least those sections.
size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset();
size_t text_size = oat_writer->GetSize() - rodata_size;
- elf_writer->PrepareDebugInfo(rodata_size, text_size, oat_writer->GetMethodDebugInfo());
+ elf_writer->SetLoadedSectionSizes(rodata_size, text_size, oat_writer->GetBssSize());
+
+ if (IsImage()) {
+ // Update oat layout.
+ DCHECK(image_writer_ != nullptr);
+ DCHECK_LT(i, oat_filenames_.size());
+ image_writer_->UpdateOatFileLayout(i,
+ elf_writer->GetLoadedSize(),
+ oat_writer->GetOatDataOffset(),
+ oat_writer->GetSize());
+ }
+ }
+
+ for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
+ std::unique_ptr<File>& oat_file = oat_files_[i];
+ std::unique_ptr<ElfWriter>& elf_writer = elf_writers_[i];
+ std::unique_ptr<OatWriter>& oat_writer = oat_writers_[i];
+
+ // We need to mirror the layout of the ELF file in the compressed debug-info.
+ // Therefore PrepareDebugInfo() relies on the SetLoadedSectionSizes() call further above.
+ elf_writer->PrepareDebugInfo(oat_writer->GetMethodDebugInfo());
OutputStream*& rodata = rodata_[i];
DCHECK(rodata != nullptr);
@@ -1717,7 +1692,13 @@ class Dex2Oat FINAL {
return false;
}
- elf_writer->SetBssSize(oat_writer->GetBssSize());
+ if (IsImage()) {
+ // Update oat header information.
+ DCHECK(image_writer_ != nullptr);
+ DCHECK_LT(i, oat_filenames_.size());
+ image_writer_->UpdateOatFileHeader(i, oat_writer->GetOatHeader());
+ }
+
elf_writer->WriteDynamicSection();
elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo());
elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations());
@@ -1731,19 +1712,10 @@ class Dex2Oat FINAL {
if (oat_files_[i] != nullptr) {
if (oat_files_[i]->Flush() != 0) {
PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i];
- oat_files_[i]->Erase();
return false;
}
}
- if (IsImage()) {
- // Update oat estimates.
- DCHECK(image_writer_ != nullptr);
- DCHECK_LT(i, oat_filenames_.size());
-
- image_writer_->UpdateOatFile(oat_file.get(), oat_filenames_[i]);
- }
-
VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i];
oat_writer.reset();
@@ -1865,33 +1837,44 @@ class Dex2Oat FINAL {
}
bool UseProfileGuidedCompilation() const {
- return !profile_files_.empty() || !profile_files_fd_.empty();
+ return !profile_file_.empty() || (profile_file_fd_ != kInvalidFd);
}
- bool ProcessProfiles() {
+ bool LoadProfile() {
DCHECK(UseProfileGuidedCompilation());
- ProfileCompilationInfo* info = nullptr;
- bool result = false;
- if (profile_files_.empty()) {
- DCHECK(!profile_files_fd_.empty());
- result = ProfileAssistant::ProcessProfiles(
- profile_files_fd_, reference_profile_files_fd_, &info);
- CloseAllFds(profile_files_fd_, "profile_files_fd_");
- CloseAllFds(reference_profile_files_fd_, "reference_profile_files_fd_");
+
+ profile_compilation_info_.reset(new ProfileCompilationInfo());
+ ScopedFlock flock;
+ bool success = false;
+ std::string error;
+ if (profile_file_fd_ != -1) {
+ // The file doesn't need to be flushed so don't check the usage.
+ // Pass a bogus path so that we can easily attribute any reported error.
+ File file(profile_file_fd_, "profile", /*check_usage*/ false, /*read_only_mode*/ true);
+ if (flock.Init(&file, &error)) {
+ success = profile_compilation_info_->Load(profile_file_fd_);
+ }
} else {
- result = ProfileAssistant::ProcessProfiles(
- profile_files_, reference_profile_files_, &info);
+ if (flock.Init(profile_file_.c_str(), O_RDONLY, /* block */ true, &error)) {
+ success = profile_compilation_info_->Load(flock.GetFile()->Fd());
+ }
+ }
+ if (!error.empty()) {
+ LOG(WARNING) << "Cannot lock profiles: " << error;
}
- profile_compilation_info_.reset(info);
+ if (!success) {
+ profile_compilation_info_.reset(nullptr);
+ }
- return result;
+ return success;
}
bool ShouldCompileBasedOnProfiles() const {
DCHECK(UseProfileGuidedCompilation());
- // If we are given profiles, compile only if we have new information.
- return profile_compilation_info_ != nullptr;
+ // If we are given a profile, compile only if we have some data in it.
+ return (profile_compilation_info_ != nullptr) &&
+ (profile_compilation_info_->GetNumberOfMethods() != 0);
}
private:
@@ -2226,22 +2209,21 @@ class Dex2Oat FINAL {
}
if (!image_writer_->Write(app_image_fd_,
image_filenames_,
- oat_fd_,
- oat_filenames_,
- oat_location_)) {
+ oat_filenames_)) {
LOG(ERROR) << "Failure during image file creation";
return false;
}
// We need the OatDataBegin entries.
- std::map<const char*, uintptr_t> oat_data_begins;
- for (const char* oat_filename : oat_filenames_) {
- oat_data_begins.emplace(oat_filename, image_writer_->GetOatDataBegin(oat_filename));
+ dchecked_vector<uintptr_t> oat_data_begins;
+ for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
+ oat_data_begins.push_back(image_writer_->GetOatDataBegin(i));
}
// Destroy ImageWriter before doing FixupElf.
image_writer_.reset();
- for (const char* oat_filename : oat_filenames_) {
+ for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
+ const char* oat_filename = oat_filenames_[i];
// Do not fix up the ELF file if we are --compile-pic or compiling the app image
if (!compiler_options_->GetCompilePic() && IsBootImage()) {
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename));
@@ -2250,9 +2232,7 @@ class Dex2Oat FINAL {
return false;
}
- uintptr_t oat_data_begin = oat_data_begins.find(oat_filename)->second;
-
- if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) {
+ if (!ElfWriter::Fixup(oat_file.get(), oat_data_begins[i])) {
oat_file->Erase();
LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
return false;
@@ -2460,15 +2440,13 @@ class Dex2Oat FINAL {
int swap_fd_;
std::string app_image_file_name_;
int app_image_fd_;
- std::vector<std::string> profile_files_;
- std::vector<std::string> reference_profile_files_;
- std::vector<uint32_t> profile_files_fd_;
- std::vector<uint32_t> reference_profile_files_fd_;
+ std::string profile_file_;
+ int profile_file_fd_;
std::unique_ptr<ProfileCompilationInfo> profile_compilation_info_;
TimingLogger* timings_;
std::unique_ptr<CumulativeLogger> compiler_phases_timings_;
std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_;
- std::unordered_map<const DexFile*, const char*> dex_file_oat_filename_map_;
+ std::unordered_map<const DexFile*, size_t> dex_file_oat_index_map_;
// Backing storage.
std::vector<std::string> char_backing_storage_;
@@ -2584,16 +2562,20 @@ static int dex2oat(int argc, char** argv) {
TimingLogger timings("compiler", false, false);
- Dex2Oat dex2oat(&timings);
+ // Allocate `dex2oat` on the heap instead of on the stack, as Clang
+ // might produce a stack frame too large for this function or for
+ // functions inlining it (such as main), that would not fit the
+ // requirements of the `-Wframe-larger-than` option.
+ std::unique_ptr<Dex2Oat> dex2oat = MakeUnique<Dex2Oat>(&timings);
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
- dex2oat.ParseArgs(argc, argv);
+ dex2oat->ParseArgs(argc, argv);
// Process profile information and assess if we need to do a profile guided compilation.
// This operation involves I/O.
- if (dex2oat.UseProfileGuidedCompilation()) {
- if (dex2oat.ProcessProfiles()) {
- if (!dex2oat.ShouldCompileBasedOnProfiles()) {
+ if (dex2oat->UseProfileGuidedCompilation()) {
+ if (dex2oat->LoadProfile()) {
+ if (!dex2oat->ShouldCompileBasedOnProfiles()) {
LOG(INFO) << "Skipped compilation because of insignificant profile delta";
return EXIT_SUCCESS;
}
@@ -2604,7 +2586,7 @@ static int dex2oat(int argc, char** argv) {
}
// Check early that the result of compilation can be written
- if (!dex2oat.OpenFile()) {
+ if (!dex2oat->OpenFile()) {
return EXIT_FAILURE;
}
@@ -2614,25 +2596,25 @@ static int dex2oat(int argc, char** argv) {
// 3) Compiling with --host
// 4) Compiling on the host (not a target build)
// Otherwise, print a stripped command line.
- if (kIsDebugBuild || dex2oat.IsBootImage() || dex2oat.IsHost() || !kIsTargetBuild) {
+ if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) {
LOG(INFO) << CommandLine();
} else {
LOG(INFO) << StrippedCommandLine();
}
- if (!dex2oat.Setup()) {
- dex2oat.EraseOatFiles();
+ if (!dex2oat->Setup()) {
+ dex2oat->EraseOatFiles();
return EXIT_FAILURE;
}
bool result;
- if (dex2oat.IsImage()) {
- result = CompileImage(dex2oat);
+ if (dex2oat->IsImage()) {
+ result = CompileImage(*dex2oat);
} else {
- result = CompileApp(dex2oat);
+ result = CompileApp(*dex2oat);
}
- dex2oat.Shutdown();
+ dex2oat->Shutdown();
return result;
}
} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index af08fc4e93..e30b968a96 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -145,7 +145,9 @@ class OatSymbolizer FINAL {
bss->WriteNoBitsSection(oat_file_->BssSize());
}
- builder_->WriteDynamicSection(elf_file->GetPath());
+ builder_->PrepareDynamicSection(
+ elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize());
+ builder_->WriteDynamicSection();
Walk(&art::OatSymbolizer::RegisterForDedup);
diff --git a/profman/Android.mk b/profman/Android.mk
new file mode 100644
index 0000000000..d38d107d21
--- /dev/null
+++ b/profman/Android.mk
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include art/build/Android.executable.mk
+
+PROFMAN_SRC_FILES := \
+ profman.cc \
+ profile_assistant.cc
+
+# TODO: Remove this when the framework (installd) supports pushing the
+# right instruction-set parameter for the primary architecture.
+ifneq ($(filter ro.zygote=zygote64,$(PRODUCT_DEFAULT_PROPERTY_OVERRIDES)),)
+ profman_arch := 64
+else
+ profman_arch := 32
+endif
+
+ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
+ $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,ndebug,$(profman_arch)))
+endif
+ifeq ($(ART_BUILD_TARGET_DEBUG),true)
+ $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,target,debug,$(profman_arch)))
+endif
+
+ifeq ($(ART_BUILD_HOST_NDEBUG),true)
+ $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,ndebug))
+endif
+ifeq ($(ART_BUILD_HOST_DEBUG),true)
+ $(eval $(call build-art-executable,profman,$(PROFMAN_SRC_FILES),libcutils,art/profman,host,debug))
+endif
diff --git a/compiler/profile_assistant.cc b/profman/profile_assistant.cc
index 85335efcc4..58e8a3a7d1 100644
--- a/compiler/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,13 +24,10 @@ namespace art {
// Minimum number of new methods that profiles must contain to enable recompilation.
static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
-bool ProfileAssistant::ProcessProfilesInternal(
+ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
- const std::vector<ScopedFlock>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
+ const ScopedFlock& reference_profile_file) {
DCHECK(!profile_files.empty());
- DCHECK(!reference_profile_files.empty() ||
- (profile_files.size() == reference_profile_files.size()));
std::vector<ProfileCompilationInfo> new_info(profile_files.size());
bool should_compile = false;
@@ -38,51 +35,53 @@ bool ProfileAssistant::ProcessProfilesInternal(
for (size_t i = 0; i < new_info.size(); i++) {
if (!new_info[i].Load(profile_files[i].GetFile()->Fd())) {
LOG(WARNING) << "Could not load profile file at index " << i;
- return false;
+ return kErrorBadProfiles;
}
// Do we have enough new profiled methods that will make the compilation worthwhile?
should_compile |= (new_info[i].GetNumberOfMethods() > kMinNewMethodsForCompilation);
}
if (!should_compile) {
- return true;
+ return kSkipCompilation;
}
- std::unique_ptr<ProfileCompilationInfo> result(new ProfileCompilationInfo());
// Merge information.
+ ProfileCompilationInfo info;
+ if (!info.Load(reference_profile_file.GetFile()->Fd())) {
+ LOG(WARNING) << "Could not load reference profile file";
+ return kErrorBadProfiles;
+ }
+
for (size_t i = 0; i < new_info.size(); i++) {
- if (!reference_profile_files.empty()) {
- if (!new_info[i].Load(reference_profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not load reference profile file at index " << i;
- return false;
- }
- }
// Merge all data into a single object.
- if (!result->Load(new_info[i])) {
+ if (!info.Load(new_info[i])) {
LOG(WARNING) << "Could not merge profile data at index " << i;
- return false;
+ return kErrorBadProfiles;
}
}
- // We were successful in merging all profile information. Update the files.
- for (size_t i = 0; i < new_info.size(); i++) {
- if (!reference_profile_files.empty()) {
- if (!reference_profile_files[i].GetFile()->ClearContent()) {
- PLOG(WARNING) << "Could not clear reference profile file at index " << i;
- return false;
- }
- if (!new_info[i].Save(reference_profile_files[i].GetFile()->Fd())) {
- LOG(WARNING) << "Could not save reference profile file at index " << i;
- return false;
- }
- if (!profile_files[i].GetFile()->ClearContent()) {
- PLOG(WARNING) << "Could not clear profile file at index " << i;
- return false;
- }
- }
+ // We were successful in merging all profile information. Update the reference profile.
+ if (!reference_profile_file.GetFile()->ClearContent()) {
+ PLOG(WARNING) << "Could not clear reference profile file";
+ return kErrorIO;
}
+ if (!info.Save(reference_profile_file.GetFile()->Fd())) {
+ LOG(WARNING) << "Could not save reference profile file";
+ return kErrorIO;
+ }
+
+ return kCompile;
+}
- *profile_compilation_info = result.release();
- return true;
+static bool InitFlock(const std::string& filename, ScopedFlock& flock, std::string* error) {
+ return flock.Init(filename.c_str(), O_RDWR, /* block */ true, error);
+}
+
+static bool InitFlock(int fd, ScopedFlock& flock, std::string* error) {
+ DCHECK_GE(fd, 0);
+ // We do not own the descriptor, so disable auto-close and don't check usage.
+ File file(fd, false);
+ file.DisableAutoClose();
+ return flock.Init(&file, error);
}
class ScopedCollectionFlock {
@@ -92,7 +91,7 @@ class ScopedCollectionFlock {
// Will block until all the locks are acquired.
bool Init(const std::vector<std::string>& filenames, /* out */ std::string* error) {
for (size_t i = 0; i < filenames.size(); i++) {
- if (!flocks_[i].Init(filenames[i].c_str(), O_RDWR, /* block */ true, error)) {
+ if (!InitFlock(filenames[i], flocks_[i], error)) {
*error += " (index=" + std::to_string(i) + ")";
return false;
}
@@ -101,12 +100,10 @@ class ScopedCollectionFlock {
}
// Will block until all the locks are acquired.
- bool Init(const std::vector<uint32_t>& fds, /* out */ std::string* error) {
+ bool Init(const std::vector<int>& fds, /* out */ std::string* error) {
for (size_t i = 0; i < fds.size(); i++) {
- // We do not own the descriptor, so disable auto-close and don't check usage.
- File file(fds[i], false);
- file.DisableAutoClose();
- if (!flocks_[i].Init(&file, error)) {
+ DCHECK_GE(fds[i], 0);
+ if (!InitFlock(fds[i], flocks_[i], error)) {
*error += " (index=" + std::to_string(i) + ")";
return false;
}
@@ -120,50 +117,43 @@ class ScopedCollectionFlock {
std::vector<ScopedFlock> flocks_;
};
-bool ProfileAssistant::ProcessProfiles(
- const std::vector<uint32_t>& profile_files_fd,
- const std::vector<uint32_t>& reference_profile_files_fd,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
- *profile_compilation_info = nullptr;
-
+ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ const std::vector<int>& profile_files_fd,
+ int reference_profile_file_fd) {
+ DCHECK_GE(reference_profile_file_fd, 0);
std::string error;
ScopedCollectionFlock profile_files_flocks(profile_files_fd.size());
if (!profile_files_flocks.Init(profile_files_fd, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return false;
+ return kErrorCannotLock;
}
- ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files_fd.size());
- if (!reference_profile_files_flocks.Init(reference_profile_files_fd, &error)) {
- LOG(WARNING) << "Could not lock reference profile files: " << error;
- return false;
+ ScopedFlock reference_profile_file_flock;
+ if (!InitFlock(reference_profile_file_fd, reference_profile_file_flock, &error)) {
+ LOG(WARNING) << "Could not lock reference profiled files: " << error;
+ return kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files_flocks.Get(),
- reference_profile_files_flocks.Get(),
- profile_compilation_info);
+ reference_profile_file_flock);
}
-bool ProfileAssistant::ProcessProfiles(
+ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<std::string>& profile_files,
- const std::vector<std::string>& reference_profile_files,
- /*out*/ ProfileCompilationInfo** profile_compilation_info) {
- *profile_compilation_info = nullptr;
-
+ const std::string& reference_profile_file) {
std::string error;
ScopedCollectionFlock profile_files_flocks(profile_files.size());
if (!profile_files_flocks.Init(profile_files, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return false;
+ return kErrorCannotLock;
}
- ScopedCollectionFlock reference_profile_files_flocks(reference_profile_files.size());
- if (!reference_profile_files_flocks.Init(reference_profile_files, &error)) {
+ ScopedFlock reference_profile_file_flock;
+ if (!InitFlock(reference_profile_file, reference_profile_file_flock, &error)) {
LOG(WARNING) << "Could not lock reference profile files: " << error;
- return false;
+ return kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files_flocks.Get(),
- reference_profile_files_flocks.Get(),
- profile_compilation_info);
+ reference_profile_file_flock);
}
} // namespace art
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
new file mode 100644
index 0000000000..d3c75b817a
--- /dev/null
+++ b/profman/profile_assistant.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_PROFMAN_PROFILE_ASSISTANT_H_
+#define ART_PROFMAN_PROFILE_ASSISTANT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/scoped_flock.h"
+#include "jit/offline_profiling_info.h"
+
+namespace art {
+
+class ProfileAssistant {
+ public:
+ // These also serve as return codes of profman and are processed by installd
+ // (frameworks/native/cmds/installd/commands.cpp)
+ enum ProcessingResult {
+ kCompile = 0,
+ kSkipCompilation = 1,
+ kErrorBadProfiles = 2,
+ kErrorIO = 3,
+ kErrorCannotLock = 4
+ };
+
+ // Process the profile information present in the given files. Returns one of
+ // ProcessingResult values depending on profile information and whether or not
+ // the analysis ended up successfully (i.e. no errors during reading,
+ // merging or writing of profile files).
+ //
+ // When the returned value is kCompile there is a significant difference
+ // between profile_files and reference_profile_files. In this case
+ // reference_profile will be updated with the profiling info obtain after
+ // merging all profiles.
+ //
+ // When the returned value is kSkipCompilation, the difference between the
+ // merge of the current profiles and the reference one is insignificant. In
+ // this case no file will be updated.
+ //
+ static ProcessingResult ProcessProfiles(
+ const std::vector<std::string>& profile_files,
+ const std::string& reference_profile_file);
+
+ static ProcessingResult ProcessProfiles(
+ const std::vector<int>& profile_files_fd_,
+ int reference_profile_file_fd);
+
+ private:
+ static ProcessingResult ProcessProfilesInternal(
+ const std::vector<ScopedFlock>& profile_files,
+ const ScopedFlock& reference_profile_file);
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileAssistant);
+};
+
+} // namespace art
+
+#endif // ART_PROFMAN_PROFILE_ASSISTANT_H_
diff --git a/compiler/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index 58b7513377..3faa8eb53f 100644
--- a/compiler/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -18,8 +18,9 @@
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
-#include "compiler/profile_assistant.h"
+#include "profile_assistant.h"
#include "jit/offline_profiling_info.h"
+#include "utils.h"
namespace art {
@@ -44,23 +45,47 @@ class ProfileAssistantTest : public CommonRuntimeTest {
ASSERT_TRUE(profile.GetFile()->ResetOffset());
}
- uint32_t GetFd(const ScratchFile& file) const {
- return static_cast<uint32_t>(file.GetFd());
+ int GetFd(const ScratchFile& file) const {
+ return static_cast<int>(file.GetFd());
+ }
+
+ void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) {
+ ProfileCompilationInfo file_info;
+ ASSERT_TRUE(file.GetFile()->ResetOffset());
+ ASSERT_TRUE(file_info.Load(GetFd(file)));
+ ASSERT_TRUE(file_info.Equals(info));
+ }
+
+ // Runs test with given arguments.
+ int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) {
+ std::string file_path = GetTestAndroidRoot();
+ file_path += "/bin/profman";
+ if (kIsDebugBuild) {
+ file_path += "d";
+ }
+
+ EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ std::vector<std::string> argv_str;
+ argv_str.push_back(file_path);
+ for (size_t k = 0; k < profiles_fd.size(); k++) {
+ argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k]));
+ }
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd));
+
+ std::string error;
+ return ExecAndReturnCode(argv_str, &error);
}
};
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
ScratchFile profile1;
ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
+ ScratchFile reference_profile;
- std::vector<uint32_t> profile_fds({
+ std::vector<int> profile_fds({
GetFd(profile1),
GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
+ int reference_profile_fd = GetFd(reference_profile);
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
ProfileCompilationInfo info1;
@@ -69,44 +94,32 @@ TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
// We should advise compilation.
- ProfileCompilationInfo* result;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result != nullptr);
-
+ ASSERT_EQ(ProfileAssistant::kCompile,
+ ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(result.Load(reference_profile_fd));
+
ProfileCompilationInfo expected;
ASSERT_TRUE(expected.Load(info1));
ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Equals(*result));
+ ASSERT_TRUE(expected.Equals(result));
- // The information from profiles must be transfered to the reference profiles.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2)));
- ASSERT_TRUE(file_info2.Equals(info2));
-
- // Initial profiles must be cleared.
- ASSERT_EQ(0, profile1.GetFile()->GetLength());
- ASSERT_EQ(0, profile2.GetFile()->GetLength());
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+ CheckProfileInfo(profile2, info2);
}
TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
ScratchFile profile1;
ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
+ ScratchFile reference_profile;
- std::vector<uint32_t> profile_fds({
+ std::vector<int> profile_fds({
GetFd(profile1),
GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
+ int reference_profile_fd = GetFd(reference_profile);
// The new profile info will contain the methods with indices 0-100.
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
@@ -118,60 +131,39 @@ TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
// The reference profile info will contain the methods with indices 50-150.
const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
- ProfileCompilationInfo reference_info1;
- SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile1,
- &reference_info1, kNumberOfMethodsToEnableCompilation / 2);
- ProfileCompilationInfo reference_info2;
- SetupProfile("p2", 2, kNumberOfMethodsAlreadyCompiled, reference_profile2,
- &reference_info2, kNumberOfMethodsToEnableCompilation / 2);
+ ProfileCompilationInfo reference_info;
+ SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, reference_profile,
+ &reference_info, kNumberOfMethodsToEnableCompilation / 2);
// We should advise compilation.
- ProfileCompilationInfo* result;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result != nullptr);
+ ASSERT_EQ(ProfileAssistant::kCompile,
+ ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs
+ ProfileCompilationInfo result;
+ ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(result.Load(reference_profile_fd));
+
ProfileCompilationInfo expected;
ASSERT_TRUE(expected.Load(info1));
ASSERT_TRUE(expected.Load(info2));
- ASSERT_TRUE(expected.Load(reference_info1));
- ASSERT_TRUE(expected.Load(reference_info2));
- ASSERT_TRUE(expected.Equals(*result));
-
- // The information from profiles must be transfered to the reference profiles.
- ProfileCompilationInfo file_info1;
- ProfileCompilationInfo merge1;
- ASSERT_TRUE(merge1.Load(info1));
- ASSERT_TRUE(merge1.Load(reference_info1));
- ASSERT_TRUE(reference_profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(reference_profile1)));
- ASSERT_TRUE(file_info1.Equals(merge1));
+ ASSERT_TRUE(expected.Load(reference_info));
+ ASSERT_TRUE(expected.Equals(result));
- ProfileCompilationInfo file_info2;
- ProfileCompilationInfo merge2;
- ASSERT_TRUE(merge2.Load(info2));
- ASSERT_TRUE(merge2.Load(reference_info2));
- ASSERT_TRUE(reference_profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile2)));
- ASSERT_TRUE(file_info2.Equals(merge2));
-
- // Initial profiles must be cleared.
- ASSERT_EQ(0, profile1.GetFile()->GetLength());
- ASSERT_EQ(0, profile2.GetFile()->GetLength());
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+ CheckProfileInfo(profile2, info2);
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
ScratchFile profile1;
ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
+ ScratchFile reference_profile;
- std::vector<uint32_t> profile_fds({
+ std::vector<int> profile_fds({
GetFd(profile1),
GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
+ int reference_profile_fd = GetFd(reference_profile);
const uint16_t kNumberOfMethodsToSkipCompilation = 1;
ProfileCompilationInfo info1;
@@ -180,9 +172,8 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, profile2, &info2);
// We should not advise compilation.
- ProfileCompilationInfo* result = nullptr;
- ASSERT_TRUE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result == nullptr);
+ ASSERT_EQ(ProfileAssistant::kSkipCompilation,
+ ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
ProfileCompilationInfo file_info1;
@@ -196,22 +187,22 @@ TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
ASSERT_TRUE(file_info2.Equals(info2));
// Reference profile files must remain empty.
- ASSERT_EQ(0, reference_profile1.GetFile()->GetLength());
- ASSERT_EQ(0, reference_profile2.GetFile()->GetLength());
+ ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
+
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+ CheckProfileInfo(profile2, info2);
}
TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
ScratchFile profile1;
ScratchFile profile2;
- ScratchFile reference_profile1;
- ScratchFile reference_profile2;
+ ScratchFile reference_profile;
- std::vector<uint32_t> profile_fds({
+ std::vector<int> profile_fds({
GetFd(profile1),
GetFd(profile2)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile1),
- GetFd(reference_profile2)});
+ int reference_profile_fd = GetFd(reference_profile);
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
// Assign different hashes for the same dex file. This will make merging of information to fail.
@@ -221,34 +212,24 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, profile2, &info2);
// We should fail processing.
- ProfileCompilationInfo* result = nullptr;
- ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result == nullptr);
+ ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
+ ProcessProfiles(profile_fds, reference_profile_fd));
- // The information from profiles must still remain the same.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
-
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(profile2.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
- ASSERT_TRUE(file_info2.Equals(info2));
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
+ CheckProfileInfo(profile2, info2);
// Reference profile files must still remain empty.
- ASSERT_EQ(0, reference_profile1.GetFile()->GetLength());
- ASSERT_EQ(0, reference_profile2.GetFile()->GetLength());
+ ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
}
TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
ScratchFile profile1;
ScratchFile reference_profile;
- std::vector<uint32_t> profile_fds({
+ std::vector<int> profile_fds({
GetFd(profile1)});
- std::vector<uint32_t> reference_profile_fds({
- GetFd(reference_profile)});
+ int reference_profile_fd = GetFd(reference_profile);
const uint16_t kNumberOfMethodsToEnableCompilation = 100;
// Assign different hashes for the same dex file. This will make merging of information to fail.
@@ -258,22 +239,13 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, reference_profile, &reference_info);
// We should not advise compilation.
- ProfileCompilationInfo* result = nullptr;
ASSERT_TRUE(profile1.GetFile()->ResetOffset());
ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
- ASSERT_FALSE(ProfileAssistant::ProcessProfiles(profile_fds, reference_profile_fds, &result));
- ASSERT_TRUE(result == nullptr);
-
- // The information from profiles must still remain the same.
- ProfileCompilationInfo file_info1;
- ASSERT_TRUE(profile1.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
- ASSERT_TRUE(file_info1.Equals(info1));
+ ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
+ ProcessProfiles(profile_fds, reference_profile_fd));
- ProfileCompilationInfo file_info2;
- ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
- ASSERT_TRUE(file_info2.Load(GetFd(reference_profile)));
- ASSERT_TRUE(file_info2.Equals(reference_info));
+ // The information from profiles must remain the same.
+ CheckProfileInfo(profile1, info1);
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
new file mode 100644
index 0000000000..7c9e449ed5
--- /dev/null
+++ b/profman/profman.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/dumpable.h"
+#include "base/scoped_flock.h"
+#include "base/stringpiece.h"
+#include "base/stringprintf.h"
+#include "base/time_utils.h"
+#include "base/unix_file/fd_file.h"
+#include "jit/offline_profiling_info.h"
+#include "utils.h"
+#include "profile_assistant.h"
+
+namespace art {
+
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+ std::vector<std::string> command;
+ for (int i = 0; i < original_argc; ++i) {
+ command.push_back(original_argv[i]);
+ }
+ return Join(command, ' ');
+}
+
+static void UsageErrorV(const char* fmt, va_list ap) {
+ std::string error;
+ StringAppendV(&error, fmt, ap);
+ LOG(ERROR) << error;
+}
+
+static void UsageError(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+}
+
+NO_RETURN static void Usage(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+
+ UsageError("Command: %s", CommandLine().c_str());
+ UsageError("Usage: profman [options]...");
+ UsageError("");
+ UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
+ UsageError(" Can be specified multiple time, in which case the data from the different");
+ UsageError(" profiles will be aggregated.");
+ UsageError("");
+ UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
+ UsageError(" Cannot be used together with --profile-file.");
+ UsageError("");
+ UsageError(" --reference-profile-file=<filename>: specify a reference profile.");
+ UsageError(" The data in this file will be compared with the data obtained by merging");
+ UsageError(" all the files specified with --profile-file or --profile-file-fd.");
+ UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into");
+ UsageError(" --reference-profile-file. ");
+ UsageError("");
+ UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
+ UsageError(" accepts a file descriptor. Cannot be used together with");
+ UsageError(" --reference-profile-file.");
+ UsageError("");
+
+ exit(EXIT_FAILURE);
+}
+
+class ProfMan FINAL {
+ public:
+ ProfMan() :
+ reference_profile_file_fd_(-1),
+ start_ns_(NanoTime()) {}
+
+ ~ProfMan() {
+ LogCompletionTime();
+ }
+
+ void ParseArgs(int argc, char **argv) {
+ original_argc = argc;
+ original_argv = argv;
+
+ InitLogging(argv);
+
+ // Skip over the command name.
+ argv++;
+ argc--;
+
+ if (argc == 0) {
+ Usage("No arguments specified");
+ }
+
+ for (int i = 0; i < argc; ++i) {
+ const StringPiece option(argv[i]);
+ const bool log_options = false;
+ if (log_options) {
+ LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i];
+ }
+ if (option.starts_with("--profile-file=")) {
+ profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
+ } else if (option.starts_with("--profile-file-fd=")) {
+ ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
+ } else if (option.starts_with("--reference-profile-file=")) {
+ reference_profile_file_ = option.substr(strlen("--reference-profile-file=")).ToString();
+ } else if (option.starts_with("--reference-profile-file-fd=")) {
+ ParseUintOption(option, "--reference-profile-file-fd", &reference_profile_file_fd_, Usage);
+ } else {
+ Usage("Unknown argument %s", option.data());
+ }
+ }
+
+ if (profile_files_.empty() && profile_files_fd_.empty()) {
+ Usage("No profile files specified.");
+ }
+ if (!profile_files_.empty() && !profile_files_fd_.empty()) {
+ Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
+ }
+ if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) {
+ Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd");
+ }
+ if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) {
+ Usage("Reference profile file not specified");
+ }
+ }
+
+ ProfileAssistant::ProcessingResult ProcessProfiles() {
+ ProfileAssistant::ProcessingResult result;
+ if (profile_files_.empty()) {
+ // The file doesn't need to be flushed here (ProcessProfiles will do it)
+ // so don't check the usage.
+ File file(reference_profile_file_fd_, false);
+ result = ProfileAssistant::ProcessProfiles(profile_files_fd_, reference_profile_file_fd_);
+ CloseAllFds(profile_files_fd_, "profile_files_fd_");
+ } else {
+ result = ProfileAssistant::ProcessProfiles(profile_files_, reference_profile_file_);
+ }
+ return result;
+ }
+
+ private:
+ static void ParseFdForCollection(const StringPiece& option,
+ const char* arg_name,
+ std::vector<int>* fds) {
+ int fd;
+ ParseUintOption(option, arg_name, &fd, Usage);
+ fds->push_back(fd);
+ }
+
+ static void CloseAllFds(const std::vector<int>& fds, const char* descriptor) {
+ for (size_t i = 0; i < fds.size(); i++) {
+ if (close(fds[i]) < 0) {
+ PLOG(WARNING) << "Failed to close descriptor for " << descriptor << " at index " << i;
+ }
+ }
+ }
+
+ void LogCompletionTime() {
+ LOG(INFO) << "profman took " << PrettyDuration(NanoTime() - start_ns_);
+ }
+
+ std::vector<std::string> profile_files_;
+ std::vector<int> profile_files_fd_;
+ std::string reference_profile_file_;
+ int reference_profile_file_fd_;
+ uint64_t start_ns_;
+};
+
+// See ProfileAssistant::ProcessingResult for return codes.
+static int profman(int argc, char** argv) {
+ ProfMan profman;
+
+ // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
+ profman.ParseArgs(argc, argv);
+
+ // Process profile information and assess if we need to do a profile guided compilation.
+ // This operation involves I/O.
+ return profman.ProcessProfiles();
+}
+
+} // namespace art
+
+int main(int argc, char **argv) {
+ return art::profman(argc, argv);
+}
+
diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h
index 1096af0131..ae01bd5d18 100644
--- a/runtime/arch/mips/registers_mips.h
+++ b/runtime/arch/mips/registers_mips.h
@@ -100,6 +100,7 @@ enum FRegister {
F29 = 29,
F30 = 30,
F31 = 31,
+ FTMP = F8, // scratch register
kNumberOfFRegisters = 32,
kNoFRegister = -1,
};
diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h
index b027c955bf..81fae72b44 100644
--- a/runtime/arch/mips64/registers_mips64.h
+++ b/runtime/arch/mips64/registers_mips64.h
@@ -101,6 +101,7 @@ enum FpuRegister {
F29 = 29,
F30 = 30,
F31 = 31,
+ FTMP = F8, // scratch register
kNumberOfFpuRegisters = 32,
kNoFpuRegister = -1,
};
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index d4b873e441..d5807e27b5 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -175,12 +175,16 @@ class StubTest : public CommonRuntimeTest {
#elif defined(__aarch64__)
__asm__ __volatile__(
// Spill x0-x7 which we say we don't clobber. May contain args.
- "sub sp, sp, #64\n\t"
- ".cfi_adjust_cfa_offset 64\n\t"
+ "sub sp, sp, #80\n\t"
+ ".cfi_adjust_cfa_offset 80\n\t"
"stp x0, x1, [sp]\n\t"
"stp x2, x3, [sp, #16]\n\t"
"stp x4, x5, [sp, #32]\n\t"
"stp x6, x7, [sp, #48]\n\t"
+ // To be extra defensive, store x20. We do this because some of the stubs might make a
+ // transition into the runtime via the blr instruction below and *not* save x20.
+ "str x20, [sp, #64]\n\t"
+ // 8 byte buffer
"sub sp, sp, #16\n\t" // Reserve stack space, 16B aligned
".cfi_adjust_cfa_offset 16\n\t"
@@ -279,8 +283,9 @@ class StubTest : public CommonRuntimeTest {
"ldp x2, x3, [sp, #16]\n\t"
"ldp x4, x5, [sp, #32]\n\t"
"ldp x6, x7, [sp, #48]\n\t"
- "add sp, sp, #64\n\t" // Free stack space, now sp as on entry
- ".cfi_adjust_cfa_offset -64\n\t"
+ "ldr x20, [sp, #64]\n\t"
+ "add sp, sp, #80\n\t" // Free stack space, now sp as on entry
+ ".cfi_adjust_cfa_offset -80\n\t"
"str x9, %[fpr_result]\n\t" // Store the FPR comparison result
"mov %[result], x8\n\t" // Store the call result
@@ -298,13 +303,17 @@ class StubTest : public CommonRuntimeTest {
// Use the result from r0
: [arg0] "0"(arg0), [arg1] "r"(arg1), [arg2] "r"(arg2), [code] "r"(code), [self] "r"(self),
[referrer] "r"(referrer), [hidden] "r"(hidden), [fpr_result] "m" (fpr_result)
- : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19", "x20",
+ // Leave one register unclobbered, which is needed for compiling with
+ // -fstack-protector-strong. According to AAPCS64 registers x9-x15 are caller-saved,
+ // which means we should unclobber one of the callee-saved registers that are unused.
+ // Here we use x20.
+ : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19",
"x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x30",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
"d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31",
- "memory"); // clobber.
+ "memory");
#elif defined(__mips__) && !defined(__LP64__)
__asm__ __volatile__ (
// Spill a0-a3 and t0-t7 which we say we don't clobber. May contain args.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 078a978505..ec00a7b900 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -132,6 +132,14 @@ class ArtMethod FINAL {
return (GetAccessFlags() & kAccFinal) != 0;
}
+ bool IsCopied() {
+ const bool copied = (GetAccessFlags() & kAccCopied) != 0;
+ // (IsMiranda() || IsDefaultConflicting()) implies copied
+ DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied)
+ << "Miranda or default-conflict methods must always be copied.";
+ return copied;
+ }
+
bool IsMiranda() {
return (GetAccessFlags() & kAccMiranda) != 0;
}
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 771b2d0509..a4b38ea963 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -222,6 +222,10 @@ ArenaPool::ArenaPool(bool use_malloc, bool low_4gb)
}
ArenaPool::~ArenaPool() {
+ ReclaimMemory();
+}
+
+void ArenaPool::ReclaimMemory() {
while (free_arenas_ != nullptr) {
auto* arena = free_arenas_;
free_arenas_ = free_arenas_->next_;
@@ -229,6 +233,11 @@ ArenaPool::~ArenaPool() {
}
}
+void ArenaPool::LockReclaimMemory() {
+ MutexLock lock(Thread::Current(), lock_);
+ ReclaimMemory();
+}
+
Arena* ArenaPool::AllocArena(size_t size) {
Thread* self = Thread::Current();
Arena* ret = nullptr;
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 36334c4129..8a96571e99 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -276,6 +276,8 @@ class ArenaPool {
Arena* AllocArena(size_t size) REQUIRES(!lock_);
void FreeArenaChain(Arena* first) REQUIRES(!lock_);
size_t GetBytesAllocated() const REQUIRES(!lock_);
+ void ReclaimMemory() NO_THREAD_SAFETY_ANALYSIS;
+ void LockReclaimMemory() REQUIRES(!lock_);
// Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works
// use_malloc is false.
void TrimMaps() REQUIRES(!lock_);
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index de46b0c118..8aaeaac0dc 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -37,6 +37,7 @@ enum LogSeverity {
// and the "-verbose:" command line argument.
struct LogVerbosity {
bool class_linker; // Enabled with "-verbose:class".
+ bool collector;
bool compiler;
bool deopt;
bool gc;
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 814cbd093a..0e8031f4f2 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -83,7 +83,7 @@ bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string*
}
bool ScopedFlock::Init(File* file, std::string* error_msg) {
- file_.reset(new File(dup(file->Fd()), true));
+ file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode()));
if (file_->Fd() == -1) {
file_.reset();
*error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
@@ -114,7 +114,13 @@ ScopedFlock::~ScopedFlock() {
if (file_.get() != nullptr) {
int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
CHECK_EQ(0, flock_result);
- if (file_->FlushCloseOrErase() != 0) {
+ int close_result = -1;
+ if (file_->ReadOnlyMode()) {
+ close_result = file_->Close();
+ } else {
+ close_result = file_->FlushCloseOrErase();
+ }
+ if (close_result != 0) {
PLOG(WARNING) << "Could not close scoped file lock file.";
}
}
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index e17bebb4fb..4672948f31 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -35,18 +35,22 @@
namespace unix_file {
-FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) {
+FdFile::FdFile()
+ : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true), read_only_mode_(false) {
}
FdFile::FdFile(int fd, bool check_usage)
: guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
- fd_(fd), auto_close_(true) {
+ fd_(fd), auto_close_(true), read_only_mode_(false) {
}
FdFile::FdFile(int fd, const std::string& path, bool check_usage)
+ : FdFile(fd, path, check_usage, false) {
+}
+
+FdFile::FdFile(int fd, const std::string& path, bool check_usage, bool read_only_mode)
: guard_state_(check_usage ? GuardState::kBase : GuardState::kNoCheck),
- fd_(fd), file_path_(path), auto_close_(true) {
- CHECK_NE(0U, path.size());
+ fd_(fd), file_path_(path), auto_close_(true), read_only_mode_(read_only_mode) {
}
FdFile::~FdFile() {
@@ -99,6 +103,7 @@ bool FdFile::Open(const std::string& path, int flags) {
bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
CHECK_EQ(fd_, -1) << path;
+ read_only_mode_ = (flags & O_RDONLY) != 0;
fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
if (fd_ == -1) {
return false;
@@ -136,6 +141,7 @@ int FdFile::Close() {
}
int FdFile::Flush() {
+ DCHECK(!read_only_mode_);
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
#else
@@ -155,6 +161,7 @@ int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
}
int FdFile::SetLength(int64_t new_length) {
+ DCHECK(!read_only_mode_);
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(ftruncate64(fd_, new_length));
#else
@@ -171,6 +178,7 @@ int64_t FdFile::GetLength() const {
}
int64_t FdFile::Write(const char* buf, int64_t byte_count, int64_t offset) {
+ DCHECK(!read_only_mode_);
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(pwrite64(fd_, buf, byte_count, offset));
#else
@@ -184,6 +192,14 @@ int FdFile::Fd() const {
return fd_;
}
+bool FdFile::ReadOnlyMode() const {
+ return read_only_mode_;
+}
+
+bool FdFile::CheckUsage() const {
+ return guard_state_ != GuardState::kNoCheck;
+}
+
bool FdFile::IsOpened() const {
return fd_ >= 0;
}
@@ -219,6 +235,7 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
}
bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+ DCHECK(!read_only_mode_);
const char* ptr = static_cast<const char*>(buffer);
moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
while (byte_count > 0) {
@@ -233,6 +250,7 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
}
bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
+ DCHECK(!read_only_mode_);
off_t off = static_cast<off_t>(offset);
off_t sz = static_cast<off_t>(size);
if (offset < 0 || static_cast<int64_t>(off) != offset ||
@@ -279,12 +297,14 @@ bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
}
void FdFile::Erase() {
+ DCHECK(!read_only_mode_);
TEMP_FAILURE_RETRY(SetLength(0));
TEMP_FAILURE_RETRY(Flush());
TEMP_FAILURE_RETRY(Close());
}
int FdFile::FlushCloseOrErase() {
+ DCHECK(!read_only_mode_);
int flush_result = TEMP_FAILURE_RETRY(Flush());
if (flush_result != 0) {
LOG(::art::ERROR) << "CloseOrErase failed while flushing a file.";
@@ -301,6 +321,7 @@ int FdFile::FlushCloseOrErase() {
}
int FdFile::FlushClose() {
+ DCHECK(!read_only_mode_);
int flush_result = TEMP_FAILURE_RETRY(Flush());
if (flush_result != 0) {
LOG(::art::ERROR) << "FlushClose failed while flushing a file.";
@@ -317,6 +338,7 @@ void FdFile::MarkUnchecked() {
}
bool FdFile::ClearContent() {
+ DCHECK(!read_only_mode_);
if (SetLength(0) < 0) {
PLOG(art::ERROR) << "Failed to reset the length";
return false;
@@ -325,6 +347,7 @@ bool FdFile::ClearContent() {
}
bool FdFile::ResetOffset() {
+ DCHECK(!read_only_mode_);
off_t rc = TEMP_FAILURE_RETRY(lseek(fd_, 0, SEEK_SET));
if (rc == static_cast<off_t>(-1)) {
PLOG(art::ERROR) << "Failed to reset the offset";
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 1e2d8af151..8040afe9b7 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -37,6 +37,7 @@ class FdFile : public RandomAccessFile {
// file descriptor. (Use DisableAutoClose to retain ownership.)
FdFile(int fd, bool checkUsage);
FdFile(int fd, const std::string& path, bool checkUsage);
+ FdFile(int fd, const std::string& path, bool checkUsage, bool read_only_mode);
// Destroys an FdFile, closing the file descriptor if Close hasn't already
// been called. (If you care about the return value of Close, call it
@@ -68,6 +69,8 @@ class FdFile : public RandomAccessFile {
// Bonus API.
int Fd() const;
+ bool ReadOnlyMode() const;
+ bool CheckUsage() const;
bool IsOpened() const;
const std::string& GetPath() const {
return file_path_;
@@ -119,6 +122,7 @@ class FdFile : public RandomAccessFile {
int fd_;
std::string file_path_;
bool auto_close_;
+ bool read_only_mode_;
DISALLOW_COPY_AND_ASSIGN(FdFile);
};
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5278d1bb05..9ea082769a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -759,7 +759,7 @@ static void SanityCheckArtMethod(ArtMethod* m,
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
- } else if (m->IsMiranda()) {
+ } else if (m->IsCopied()) {
CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
@@ -1137,18 +1137,18 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*));
- const bool is_miranda = method->IsMiranda();
+ const bool is_copied = method->IsCopied();
if (resolved_types != nullptr) {
bool in_image_space = false;
- if (kIsDebugBuild || is_miranda) {
+ if (kIsDebugBuild || is_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
- DCHECK(is_miranda || in_image_space)
+ DCHECK(is_copied || in_image_space)
<< resolved_types << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
- if (!is_miranda || in_image_space) {
+ if (!is_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types),
sizeof(void*));
@@ -1157,15 +1157,15 @@ class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*));
if (resolved_methods != nullptr) {
bool in_image_space = false;
- if (kIsDebugBuild || is_miranda) {
+ if (kIsDebugBuild || is_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
- DCHECK(is_miranda || in_image_space)
+ DCHECK(is_copied || in_image_space)
<< resolved_methods << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
- if (!is_miranda || in_image_space) {
+ if (!is_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods),
sizeof(void*));
@@ -6459,7 +6459,7 @@ bool ClassLinker::LinkInterfaceMethods(
for (ArtMethod* mir_method : miranda_methods) {
ArtMethod& new_method = *out;
new_method.CopyFrom(mir_method, image_pointer_size_);
- new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda);
+ new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied);
DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u)
<< "Miranda method should be abstract!";
move_table.emplace(mir_method, &new_method);
@@ -6478,7 +6478,9 @@ bool ClassLinker::LinkInterfaceMethods(
// yet it shouldn't have methods that are skipping access checks.
// TODO This is rather arbitrary. We should maybe support classes where only some of its
// methods are skip_access_checks.
- new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccSkipAccessChecks);
+ constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
+ constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
+ new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
move_table.emplace(def_method, &new_method);
++out;
}
@@ -6489,7 +6491,7 @@ bool ClassLinker::LinkInterfaceMethods(
// this as a default, non-abstract method, since thats what it is. Also clear the
// kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
// methods that are skipping access checks.
- constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict;
+ constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
DCHECK(new_method.IsDefaultConflicting());
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 3a0f3e5065..488826b6c4 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -263,7 +263,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) {
AssertMethod(&method);
EXPECT_FALSE(method.IsDirect());
- EXPECT_TRUE(method.IsMiranda() || method.IsDefault() || method.IsDefaultConflicting());
+ EXPECT_TRUE(method.IsCopied());
EXPECT_TRUE(method.GetDeclaringClass()->IsInterface())
<< "declaring class: " << PrettyClass(method.GetDeclaringClass());
EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get()))
@@ -1225,12 +1225,12 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) {
dex_cache->SetLocation(location.Get());
const DexFile* old_dex_file = dex_cache->GetDexFile();
- DexFile* dex_file = new DexFile(old_dex_file->Begin(),
- old_dex_file->Size(),
- location->ToModifiedUtf8(),
- 0u,
- nullptr,
- nullptr);
+ std::unique_ptr<DexFile> dex_file(new DexFile(old_dex_file->Begin(),
+ old_dex_file->Size(),
+ location->ToModifiedUtf8(),
+ 0u,
+ nullptr,
+ nullptr));
{
WriterMutexLock mu(soa.Self(), *class_linker->DexLock());
// Check that inserting with a UTF16 name works.
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 369e4083a2..4de5388d8c 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -34,11 +34,7 @@ int32_t AllocRecordStackTraceElement::ComputeLineNumber() const {
const char* AllocRecord::GetClassDescriptor(std::string* storage) const {
// klass_ could contain null only if we implement class unloading.
- if (UNLIKELY(klass_.IsNull())) {
- return "null";
- } else {
- return klass_.Read()->GetDescriptor(storage);
- }
+ return klass_.IsNull() ? "null" : klass_.Read()->GetDescriptor(storage);
}
void AllocRecordObjectMap::SetProperties() {
@@ -105,8 +101,19 @@ void AllocRecordObjectMap::VisitRoots(RootVisitor* visitor) {
size_t count = recent_record_max_;
// Only visit the last recent_record_max_ number of allocation records in entries_ and mark the
// klass_ fields as strong roots.
- for (auto it = entries_.rbegin(), end = entries_.rend(); count > 0 && it != end; count--, ++it) {
- buffered_visitor.VisitRootIfNonNull(it->second->GetClassGcRoot());
+ for (auto it = entries_.rbegin(), end = entries_.rend(); it != end; ++it) {
+ AllocRecord* record = it->second;
+ if (count > 0) {
+ buffered_visitor.VisitRootIfNonNull(record->GetClassGcRoot());
+ --count;
+ }
+ // Visit all of the stack frames to make sure no methods in the stack traces get unloaded by
+ // class unloading.
+ for (size_t i = 0, depth = record->GetDepth(); i < depth; ++i) {
+ const AllocRecordStackTraceElement& element = record->StackElement(i);
+ DCHECK(element.GetMethod() != nullptr);
+ element.GetMethod()->VisitRoots(buffered_visitor, sizeof(void*));
+ }
}
}
@@ -131,12 +138,7 @@ void AllocRecordObjectMap::SweepAllocationRecords(IsMarkedVisitor* visitor) {
VLOG(heap) << "Start SweepAllocationRecords()";
size_t count_deleted = 0, count_moved = 0, count = 0;
// Only the first (size - recent_record_max_) number of records can be deleted.
- size_t delete_bound;
- if (entries_.size() <= recent_record_max_) {
- delete_bound = 0;
- } else {
- delete_bound = entries_.size() - recent_record_max_;
- }
+ const size_t delete_bound = std::max(entries_.size(), recent_record_max_) - recent_record_max_;
for (auto it = entries_.begin(), end = entries_.end(); it != end;) {
++count;
// This does not need a read barrier because this is called by GC.
@@ -187,7 +189,6 @@ struct AllocRecordStackVisitor : public StackVisitor {
SHARED_REQUIRES(Locks::mutator_lock_)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
trace(trace_in),
- depth(0),
max_depth(max) {}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -209,7 +210,7 @@ struct AllocRecordStackVisitor : public StackVisitor {
}
AllocRecordStackTrace* trace;
- size_t depth;
+ size_t depth = 0u;
const size_t max_depth;
};
@@ -237,9 +238,12 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
<< records->max_stack_depth_ << " frames, taking up to "
<< PrettySize(sz * records->alloc_record_max_) << ")";
heap->SetAllocationRecords(records);
- heap->SetAllocTrackingEnabled(true);
}
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
+ {
+ MutexLock mu(self, *Locks::alloc_tracker_lock_);
+ heap->SetAllocTrackingEnabled(true);
+ }
} else {
{
MutexLock mu(self, *Locks::alloc_tracker_lock_);
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 8b125dd167..2c487fe8df 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -58,10 +58,16 @@ RosAlloc::RosAlloc(void* base, size_t capacity, size_t max_capacity,
page_release_mode_(page_release_mode),
page_release_size_threshold_(page_release_size_threshold),
is_running_on_memory_tool_(running_on_memory_tool) {
+ DCHECK_ALIGNED(base, kPageSize);
DCHECK_EQ(RoundUp(capacity, kPageSize), capacity);
DCHECK_EQ(RoundUp(max_capacity, kPageSize), max_capacity);
CHECK_LE(capacity, max_capacity);
CHECK_ALIGNED(page_release_size_threshold_, kPageSize);
+ // Zero the memory explicitly (don't rely on that the mem map is zero-initialized).
+ if (!kMadviseZeroes) {
+ memset(base_, 0, max_capacity);
+ }
+ CHECK_EQ(madvise(base_, max_capacity, MADV_DONTNEED), 0);
if (!initialized_) {
Initialize();
}
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index a472a8bffb..b12cb5b0dd 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -192,6 +192,7 @@ class RosAlloc {
Verify();
}
DCHECK(slot != nullptr);
+ DCHECK(slot->Next() == nullptr);
Slot** headp = reinterpret_cast<Slot**>(&head_);
Slot** tailp = kUseTail ? reinterpret_cast<Slot**>(&tail_) : nullptr;
Slot* old_head = *headp;
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 8e1b7f4d1a..d393f0b1d2 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1622,7 +1622,9 @@ class ConcurrentCopyingRefFieldsVisitor {
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
ConcurrentCopyingRefFieldsVisitor visitor(this);
- to_ref->VisitReferences(visitor, visitor);
+ // Disable the read barrier for a performance reason.
+ to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+ visitor, visitor);
}
// Process a field.
diff --git a/runtime/gc/collector/immune_region.h b/runtime/gc/collector/immune_region.h
index b60426daf0..c9ac435b2b 100644
--- a/runtime/gc/collector/immune_region.h
+++ b/runtime/gc/collector/immune_region.h
@@ -66,6 +66,10 @@ class ImmuneRegion {
return end_;
}
+ size_t Size() const {
+ return size_;
+ }
+
private:
void UpdateSize() {
size_ = reinterpret_cast<uintptr_t>(end_) - reinterpret_cast<uintptr_t>(begin_);
diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc
index 8f9a9e294f..26da4ca5a9 100644
--- a/runtime/gc/collector/immune_spaces.cc
+++ b/runtime/gc/collector/immune_spaces.cc
@@ -18,6 +18,7 @@
#include "gc/space/space-inl.h"
#include "mirror/object.h"
+#include "oat_file.h"
namespace art {
namespace gc {
@@ -45,11 +46,16 @@ void ImmuneSpaces::CreateLargestImmuneRegion() {
space::ImageSpace* image_space = space->AsImageSpace();
// Update the end to include the other non-heap sections.
space_end = RoundUp(reinterpret_cast<uintptr_t>(image_space->GetImageEnd()), kPageSize);
- uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_space->GetOatFileBegin());
- uintptr_t oat_end = reinterpret_cast<uintptr_t>(image_space->GetOatFileEnd());
- if (space_end == oat_begin) {
- DCHECK_GE(oat_end, oat_begin);
- space_end = oat_end;
+ // For the app image case, GetOatFileBegin is where the oat file was mapped during image
+ // creation, the actual oat file could be somewhere else.
+ const OatFile* const image_oat_file = image_space->GetOatFile();
+ if (image_oat_file != nullptr) {
+ uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_oat_file->Begin());
+ uintptr_t oat_end = reinterpret_cast<uintptr_t>(image_oat_file->End());
+ if (space_end == oat_begin) {
+ DCHECK_GE(oat_end, oat_begin);
+ space_end = oat_end;
+ }
}
}
if (cur_begin == 0u) {
@@ -71,6 +77,8 @@ void ImmuneSpaces::CreateLargestImmuneRegion() {
}
largest_immune_region_.SetBegin(reinterpret_cast<mirror::Object*>(best_begin));
largest_immune_region_.SetEnd(reinterpret_cast<mirror::Object*>(best_end));
+ VLOG(collector) << "Immune region " << largest_immune_region_.Begin() << "-"
+ << largest_immune_region_.End();
}
void ImmuneSpaces::AddSpace(space::ContinuousSpace* space) {
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index ea290dd07d..56838f5c06 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -72,17 +72,31 @@ TEST_F(ImmuneSpacesTest, AppendBasic) {
EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), b.Limit());
}
+class DummyOatFile : public OatFile {
+ public:
+ DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) {
+ begin_ = begin;
+ end_ = end;
+ }
+};
+
class DummyImageSpace : public space::ImageSpace {
public:
- DummyImageSpace(MemMap* map, accounting::ContinuousSpaceBitmap* live_bitmap)
+ DummyImageSpace(MemMap* map,
+ accounting::ContinuousSpaceBitmap* live_bitmap,
+ std::unique_ptr<DummyOatFile>&& oat_file)
: ImageSpace("DummyImageSpace",
/*image_location*/"",
map,
live_bitmap,
- map->End()) {}
+ map->End()) {
+ oat_file_ = std::move(oat_file);
+ oat_file_non_owned_ = oat_file_.get();
+ }
- // OatSize is how large the oat file is after the image.
- static DummyImageSpace* Create(size_t size, size_t oat_size) {
+ // Size is the size of the image space, oat offset is where the oat file is located
+ // after the end of image space. oat_size is the size of the oat file.
+ static DummyImageSpace* Create(size_t size, size_t oat_offset, size_t oat_size) {
std::string error_str;
std::unique_ptr<MemMap> map(MemMap::MapAnonymous("DummyImageSpace",
nullptr,
@@ -100,6 +114,9 @@ class DummyImageSpace : public space::ImageSpace {
if (live_bitmap == nullptr) {
return nullptr;
}
+ // The actual mapped oat file may not be directly after the image for the app image case.
+ std::unique_ptr<DummyOatFile> oat_file(new DummyOatFile(map->End() + oat_offset,
+ map->End() + oat_offset + oat_size));
// Create image header.
ImageSection sections[ImageHeader::kSectionCount];
new (map->Begin()) ImageHeader(
@@ -108,6 +125,7 @@ class DummyImageSpace : public space::ImageSpace {
sections,
/*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1,
/*oat_checksum*/0u,
+ // The oat file data in the header is always right after the image space.
/*oat_file_begin*/PointerToLowMemUInt32(map->End()),
/*oat_data_begin*/PointerToLowMemUInt32(map->End()),
/*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size),
@@ -121,7 +139,7 @@ class DummyImageSpace : public space::ImageSpace {
/*is_pic*/false,
ImageHeader::kStorageModeUncompressed,
/*storage_size*/0u);
- return new DummyImageSpace(map.release(), live_bitmap.release());
+ return new DummyImageSpace(map.release(), live_bitmap.release(), std::move(oat_file));
}
};
@@ -129,7 +147,9 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) {
ImmuneSpaces spaces;
constexpr size_t image_size = 123 * kPageSize;
constexpr size_t image_oat_size = 321 * kPageSize;
- std::unique_ptr<DummyImageSpace> image_space(DummyImageSpace::Create(image_size, image_oat_size));
+ std::unique_ptr<DummyImageSpace> image_space(DummyImageSpace::Create(image_size,
+ 0,
+ image_oat_size));
ASSERT_TRUE(image_space != nullptr);
const ImageHeader& image_header = image_space->GetImageHeader();
EXPECT_EQ(image_header.GetImageSize(), image_size);
@@ -150,6 +170,18 @@ TEST_F(ImmuneSpacesTest, AppendAfterImage) {
EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()),
image_space->Begin());
EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), space.Limit());
+ // Check that appending with a gap between the map does not include the oat file.
+ image_space.reset(DummyImageSpace::Create(image_size, kPageSize, image_oat_size));
+ spaces.Reset();
+ {
+ WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
+ spaces.AddSpace(image_space.get());
+ }
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()),
+ image_space->Begin());
+ // Size should be equal, we should not add the oat file since it is not adjacent to the image
+ // space.
+ EXPECT_EQ(spaces.GetLargestImmuneRegion().Size(), image_size);
}
} // namespace collector
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3c9312f256..a656fb8faf 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -845,6 +845,13 @@ void Heap::DecrementDisableMovingGC(Thread* self) {
void Heap::IncrementDisableThreadFlip(Thread* self) {
// Supposed to be called by mutators. If thread_flip_running_ is true, block. Otherwise, go ahead.
CHECK(kUseReadBarrier);
+ bool is_nested = self->GetDisableThreadFlipCount() > 0;
+ self->IncrementDisableThreadFlipCount();
+ if (is_nested) {
+ // If this is a nested JNI critical section enter, we don't need to wait or increment the global
+ // counter. The global counter is incremented only once for a thread for the outermost enter.
+ return;
+ }
ScopedThreadStateChange tsc(self, kWaitingForGcThreadFlip);
MutexLock mu(self, *thread_flip_lock_);
bool has_waited = false;
@@ -867,10 +874,20 @@ void Heap::DecrementDisableThreadFlip(Thread* self) {
// Supposed to be called by mutators. Decrement disable_thread_flip_count_ and potentially wake up
// the GC waiting before doing a thread flip.
CHECK(kUseReadBarrier);
+ self->DecrementDisableThreadFlipCount();
+ bool is_outermost = self->GetDisableThreadFlipCount() == 0;
+ if (!is_outermost) {
+ // If this is not an outermost JNI critical exit, we don't need to decrement the global counter.
+ // The global counter is decremented only once for a thread for the outermost exit.
+ return;
+ }
MutexLock mu(self, *thread_flip_lock_);
CHECK_GT(disable_thread_flip_count_, 0U);
--disable_thread_flip_count_;
- thread_flip_cond_->Broadcast(self);
+ if (disable_thread_flip_count_ == 0) {
+ // Potentially notify the GC thread blocking to begin a thread flip.
+ thread_flip_cond_->Broadcast(self);
+ }
}
void Heap::ThreadFlipBegin(Thread* self) {
@@ -882,7 +899,8 @@ void Heap::ThreadFlipBegin(Thread* self) {
bool has_waited = false;
uint64_t wait_start = NanoTime();
CHECK(!thread_flip_running_);
- // Set this to true before waiting so that a new mutator entering a JNI critical won't starve GC.
+ // Set this to true before waiting so that frequent JNI critical enter/exits won't starve
+ // GC. This like a writer preference of a reader-writer lock.
thread_flip_running_ = true;
while (disable_thread_flip_count_ > 0) {
has_waited = true;
@@ -904,6 +922,7 @@ void Heap::ThreadFlipEnd(Thread* self) {
MutexLock mu(self, *thread_flip_lock_);
CHECK(thread_flip_running_);
thread_flip_running_ = false;
+ // Potentially notify mutator threads blocking to enter a JNI critical section.
thread_flip_cond_->Broadcast(self);
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index c02e2d3864..a181e23b53 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1113,6 +1113,8 @@ class Heap {
// Used to synchronize between JNI critical calls and the thread flip of the CC collector.
Mutex* thread_flip_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::unique_ptr<ConditionVariable> thread_flip_cond_ GUARDED_BY(thread_flip_lock_);
+ // This counter keeps track of how many threads are currently in a JNI critical section. This is
+ // incremented once per thread even with nested enters.
size_t disable_thread_flip_count_ GUARDED_BY(thread_flip_lock_);
bool thread_flip_running_ GUARDED_BY(thread_flip_lock_);
diff --git a/runtime/gc/space/memory_tool_malloc_space-inl.h b/runtime/gc/space/memory_tool_malloc_space-inl.h
index ea8b8aae5f..6cb2465539 100644
--- a/runtime/gc/space/memory_tool_malloc_space-inl.h
+++ b/runtime/gc/space/memory_tool_malloc_space-inl.h
@@ -240,9 +240,9 @@ MemoryToolMallocSpace<S,
kAdjustForRedzoneInAllocSize,
kUseObjSizeForUsable>::MemoryToolMallocSpace(
MemMap* mem_map, size_t initial_size, Params... params) : S(mem_map, initial_size, params...) {
- MEMORY_TOOL_MAKE_DEFINED(mem_map->Begin(), initial_size);
- MEMORY_TOOL_MAKE_UNDEFINED(mem_map->Begin() + initial_size,
- mem_map->Size() - initial_size);
+ // Don't want to change the valgrind states of the mem map here as the allocator is already
+ // initialized at this point and that may interfere with what the allocator does internally. Note
+ // that the tail beyond the initial size is mprotected.
}
template <typename S,
diff --git a/runtime/image.h b/runtime/image.h
index c449e43243..146ee00c84 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -143,6 +143,8 @@ class PACKED(4) ImageHeader {
oat_checksum_ = oat_checksum;
}
+ // The location that the oat file was expected to be when the image was created. The actual
+ // oat file may be at a different location for application images.
uint8_t* GetOatFileBegin() const {
return reinterpret_cast<uint8_t*>(oat_file_begin_);
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 56aeefc2f5..e3cbf53873 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -290,6 +290,14 @@ class Instrumentation {
bool IsActive() const SHARED_REQUIRES(Locks::mutator_lock_) {
return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
have_field_read_listeners_ || have_field_write_listeners_ ||
+ have_exception_caught_listeners_ || have_method_unwind_listeners_ ||
+ have_branch_listeners_ || have_invoke_virtual_or_interface_listeners_;
+ }
+
+ // Any instrumentation *other* than what is needed for Jit profiling active?
+ bool NonJitProfilingActive() const SHARED_REQUIRES(Locks::mutator_lock_) {
+ return have_dex_pc_listeners_ || have_method_exit_listeners_ ||
+ have_field_read_listeners_ || have_field_write_listeners_ ||
have_exception_caught_listeners_ || have_method_unwind_listeners_;
}
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 4fd3c78f44..a595d33f04 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -320,12 +320,13 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item,
// No Mterp variant - just use the switch interpreter.
return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
false);
+ } else if (UNLIKELY(!Runtime::Current()->IsStarted())) {
+ return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
+ false);
} else {
- const instrumentation::Instrumentation* const instrumentation =
- Runtime::Current()->GetInstrumentation();
while (true) {
- if (instrumentation->IsActive() || !Runtime::Current()->IsStarted()) {
- // TODO: allow JIT profiling instrumentation. Now, just punt on all instrumentation.
+ // Mterp does not support all instrumentation/debugging.
+ if (MterpShouldSwitchInterpreters()) {
#if !defined(__clang__)
return ExecuteGotoImpl<false, false>(self, code_item, shadow_frame, result_register);
#else
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 949112de5c..19d971ead8 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -948,11 +948,15 @@ NO_RETURN void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shad
__attribute__((cold))
SHARED_REQUIRES(Locks::mutator_lock_);
+static inline bool TraceExecutionEnabled() {
+ // Return true if you want TraceExecution invocation before each bytecode execution.
+ return false;
+}
+
static inline void TraceExecution(const ShadowFrame& shadow_frame, const Instruction* inst,
const uint32_t dex_pc)
SHARED_REQUIRES(Locks::mutator_lock_) {
- constexpr bool kTracing = false;
- if (kTracing) {
+ if (TraceExecutionEnabled()) {
#define TRACE_LOG std::cerr
std::ostringstream oss;
oss << PrettyMethod(shadow_frame.GetMethod())
diff --git a/runtime/interpreter/mterp/arm/bincmp.S b/runtime/interpreter/mterp/arm/bincmp.S
index 474bc3c276..774e1676b7 100644
--- a/runtime/interpreter/mterp/arm/bincmp.S
+++ b/runtime/interpreter/mterp/arm/bincmp.S
@@ -6,17 +6,29 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- mov${revcmp} r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ b${revcmp} .L_${opcode}_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_${opcode}_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -25,10 +37,10 @@
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- mov${revcmp} r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ mov${revcmp} rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S
index 1dba856ecb..3456a7559b 100644
--- a/runtime/interpreter/mterp/arm/footer.S
+++ b/runtime/interpreter/mterp/arm/footer.S
@@ -12,7 +12,6 @@
* has not yet been thrown. Just bail out to the reference interpreter to deal with it.
* TUNING: for consistency, we may want to just go ahead and handle these here.
*/
-#define MTERP_LOGGING 0
common_errDivideByZero:
EXPORT_PC
#if MTERP_LOGGING
@@ -103,8 +102,12 @@ MterpException:
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
add rPC, r0, #CODEITEM_INSNS_OFFSET
add rPC, rPC, r1, lsl #1 @ generate new dex_pc_ptr
- str rPC, [rFP, #OFF_FP_DEX_PC_PTR]
+ /* Do we need to switch interpreters? */
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
/* resume execution at catch block */
+ EXPORT_PC
FETCH_INST
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -116,14 +119,33 @@ MterpException:
*/
MterpCheckSuspendAndContinue:
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST)
+ bne 1f
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+1:
EXPORT_PC
mov r0, rSELF
- ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST)
- blne MterpSuspendCheck @ (self)
+ bl MterpSuspendCheck @ (self)
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpLogOSR
+#endif
+ mov r0, #1 @ Signal normal return
+ b MterpDone
+
+/*
* Bail out to reference interpreter.
*/
MterpFallback:
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index b2370bffb4..298af8a57e 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -85,6 +85,9 @@ unspecified registers or condition codes.
*/
#include "asm_support.h"
+#define MTERP_PROFILE_BRANCHES 1
+#define MTERP_LOGGING 0
+
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
#define rPC r4
@@ -109,14 +112,6 @@ unspecified registers or condition codes.
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
diff --git a/runtime/interpreter/mterp/arm/invoke.S b/runtime/interpreter/mterp/arm/invoke.S
index 7575865f1b..e47dd1b3ca 100644
--- a/runtime/interpreter/mterp/arm/invoke.S
+++ b/runtime/interpreter/mterp/arm/invoke.S
@@ -14,6 +14,9 @@
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
diff --git a/runtime/interpreter/mterp/arm/op_goto.S b/runtime/interpreter/mterp/arm/op_goto.S
index 9b3632aba2..6861950909 100644
--- a/runtime/interpreter/mterp/arm/op_goto.S
+++ b/runtime/interpreter/mterp/arm/op_goto.S
@@ -6,20 +6,28 @@
*/
/* goto +AA */
/* tuning: use sbfx for 6t2+ targets */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsl #16 @ r0<- AAxx0000
- movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended)
- add r2, r1, r1 @ r2<- byte offset, set flags
- @ If backwards branch refresh rIBASE
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended)
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r2, rINST, rINST @ r2<- byte offset, set flags
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
+ @ If backwards branch refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
mov r0, rINST, lsl #16 @ r0<- AAxx0000
- movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended)
- add r2, r1, r1 @ r2<- byte offset, set flags
+ movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended)
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r2, rINST, rINST @ r2<- byte offset, set flags
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
@ If backwards branch refresh rIBASE
bmi MterpCheckSuspendAndContinue
diff --git a/runtime/interpreter/mterp/arm/op_goto_16.S b/runtime/interpreter/mterp/arm/op_goto_16.S
index 2231acdb9e..91639ca796 100644
--- a/runtime/interpreter/mterp/arm/op_goto_16.S
+++ b/runtime/interpreter/mterp/arm/op_goto_16.S
@@ -5,17 +5,25 @@
* double to get a byte offset.
*/
/* goto/16 +AAAA */
-#if MTERP_SUSPEND
- FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended)
- adds r1, r0, r0 @ r1<- byte offset, flags set
+#if MTERP_PROFILE_BRANCHES
+ FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended)
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset, flags set
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
- FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended)
+ FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended)
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- adds r1, r0, r0 @ r1<- byte offset, flags set
+ adds r1, rINST, rINST @ r1<- byte offset, flags set
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/op_goto_32.S b/runtime/interpreter/mterp/arm/op_goto_32.S
index 6b72ff5ce2..e730b527ec 100644
--- a/runtime/interpreter/mterp/arm/op_goto_32.S
+++ b/runtime/interpreter/mterp/arm/op_goto_32.S
@@ -10,21 +10,29 @@
* offset to byte offset.
*/
/* goto/32 +AAAAAAAA */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
FETCH r0, 1 @ r0<- aaaa (lo)
FETCH r1, 2 @ r1<- AAAA (hi)
- orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa
- adds r1, r0, r0 @ r1<- byte offset
+ orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
FETCH r0, 1 @ r0<- aaaa (lo)
FETCH r1, 2 @ r1<- AAAA (hi)
+ orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa
- adds r1, r0, r0 @ r1<- byte offset
+ adds r1, rINST, rINST @ r1<- byte offset
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/op_packed_switch.S b/runtime/interpreter/mterp/arm/op_packed_switch.S
index 1e3370ea6a..4c369cb136 100644
--- a/runtime/interpreter/mterp/arm/op_packed_switch.S
+++ b/runtime/interpreter/mterp/arm/op_packed_switch.S
@@ -9,7 +9,7 @@
* for: packed-switch, sparse-switch
*/
/* op vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
FETCH r0, 1 @ r0<- bbbb (lo)
FETCH r1, 2 @ r1<- BBBB (hi)
mov r3, rINST, lsr #8 @ r3<- AA
@@ -17,9 +17,18 @@
GET_VREG r1, r3 @ r1<- vAA
add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
bl $func @ r0<- code-unit branch offset
- adds r1, r0, r0 @ r1<- byte offset; clear V
- ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ mov rINST, r0
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset; clear V
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
+ ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -30,8 +39,9 @@
GET_VREG r1, r3 @ r1<- vAA
add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
bl $func @ r0<- code-unit branch offset
+ mov rINST, r0
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- adds r1, r0, r0 @ r1<- byte offset; clear V
+ adds r1, rINST, rINST @ r1<- byte offset; clear V
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm/op_shl_long.S b/runtime/interpreter/mterp/arm/op_shl_long.S
index dc8a679cf7..12ea24883f 100644
--- a/runtime/interpreter/mterp/arm/op_shl_long.S
+++ b/runtime/interpreter/mterp/arm/op_shl_long.S
@@ -12,16 +12,16 @@
add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
GET_VREG r2, r0 @ r2<- vCC
ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
and r2, r2, #63 @ r2<- r2 & 0x3f
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
-
- mov r1, r1, asl r2 @ r1<- r1 << r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
- movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- mov r0, r0, asl r2 @ r0<- r0 << r2
+ mov r0, r0, asl r2 @ r0<- r0 << r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S
index fd7668de62..4799e77213 100644
--- a/runtime/interpreter/mterp/arm/op_shl_long_2addr.S
+++ b/runtime/interpreter/mterp/arm/op_shl_long_2addr.S
@@ -6,17 +6,17 @@
mov r3, rINST, lsr #12 @ r3<- B
ubfx r9, rINST, #8, #4 @ r9<- A
GET_VREG r2, r3 @ r2<- vB
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
and r2, r2, #63 @ r2<- r2 & 0x3f
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
-
- mov r1, r1, asl r2 @ r1<- r1 << r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
- movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
- mov r0, r0, asl r2 @ r0<- r0 << r2
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_shr_long.S b/runtime/interpreter/mterp/arm/op_shr_long.S
index c0edf90f76..88a13d6072 100644
--- a/runtime/interpreter/mterp/arm/op_shr_long.S
+++ b/runtime/interpreter/mterp/arm/op_shr_long.S
@@ -12,16 +12,16 @@
add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
GET_VREG r2, r0 @ r2<- vCC
ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
and r2, r2, #63 @ r0<- r0 & 0x3f
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
- movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- mov r1, r1, asr r2 @ r1<- r1 >> r2
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S
index ffeaf9c865..78d8bb7dba 100644
--- a/runtime/interpreter/mterp/arm/op_shr_long_2addr.S
+++ b/runtime/interpreter/mterp/arm/op_shr_long_2addr.S
@@ -6,17 +6,17 @@
mov r3, rINST, lsr #12 @ r3<- B
ubfx r9, rINST, #8, #4 @ r9<- A
GET_VREG r2, r3 @ r2<- vB
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
and r2, r2, #63 @ r2<- r2 & 0x3f
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
- movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
- mov r1, r1, asr r2 @ r1<- r1 >> r2
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_ushr_long.S b/runtime/interpreter/mterp/arm/op_ushr_long.S
index f64c861ce5..f98ec639fa 100644
--- a/runtime/interpreter/mterp/arm/op_ushr_long.S
+++ b/runtime/interpreter/mterp/arm/op_ushr_long.S
@@ -12,16 +12,16 @@
add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
GET_VREG r2, r0 @ r2<- vCC
ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
and r2, r2, #63 @ r0<- r0 & 0x3f
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
- movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S
index dbab08d817..840283dd58 100644
--- a/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S
+++ b/runtime/interpreter/mterp/arm/op_ushr_long_2addr.S
@@ -6,17 +6,17 @@
mov r3, rINST, lsr #12 @ r3<- B
ubfx r9, rINST, #8, #4 @ r9<- A
GET_VREG r2, r3 @ r2<- vB
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
and r2, r2, #63 @ r2<- r2 & 0x3f
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
- movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
- mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm/zcmp.S b/runtime/interpreter/mterp/arm/zcmp.S
index 6e9ef55104..800804d95e 100644
--- a/runtime/interpreter/mterp/arm/zcmp.S
+++ b/runtime/interpreter/mterp/arm/zcmp.S
@@ -6,25 +6,37 @@
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- mov${revcmp} r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ b${revcmp} .L_${opcode}_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_${opcode}_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- mov${revcmp} r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ mov${revcmp} rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm64/bincmp.S b/runtime/interpreter/mterp/arm64/bincmp.S
index ecab2ceba7..ed850fc49d 100644
--- a/runtime/interpreter/mterp/arm64/bincmp.S
+++ b/runtime/interpreter/mterp/arm64/bincmp.S
@@ -6,17 +6,28 @@
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- mov${condition} w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.${condition} .L_${opcode}_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_${opcode}_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -25,11 +36,11 @@
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, ${condition} // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S
index b360539a8c..aae78de1b3 100644
--- a/runtime/interpreter/mterp/arm64/footer.S
+++ b/runtime/interpreter/mterp/arm64/footer.S
@@ -10,7 +10,6 @@
* has not yet been thrown. Just bail out to the reference interpreter to deal with it.
* TUNING: for consistency, we may want to just go ahead and handle these here.
*/
-#define MTERP_LOGGING 0
common_errDivideByZero:
EXPORT_PC
#if MTERP_LOGGING
@@ -99,8 +98,11 @@ MterpException:
ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET]
add xPC, x0, #CODEITEM_INSNS_OFFSET
add xPC, xPC, x1, lsl #1 // generate new dex_pc_ptr
- str xPC, [xFP, #OFF_FP_DEX_PC_PTR]
+ /* Do we need to switch interpreters? */
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
/* resume execution at catch block */
+ EXPORT_PC
FETCH_INST
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -120,10 +122,24 @@ check1:
EXPORT_PC
mov x0, xSELF
bl MterpSuspendCheck // (self)
+ cbnz x0, MterpFallback // Something in the environment changed, switch interpreters
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpLogOSR
+#endif
+ mov x0, #1 // Signal normal return
+ b MterpDone
+
+/*
* Bail out to reference interpreter.
*/
MterpFallback:
diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S
index 351a6075cb..722375002b 100644
--- a/runtime/interpreter/mterp/arm64/header.S
+++ b/runtime/interpreter/mterp/arm64/header.S
@@ -87,6 +87,9 @@ codes.
*/
#include "asm_support.h"
+#define MTERP_PROFILE_BRANCHES 1
+#define MTERP_LOGGING 0
+
/* During bringup, we'll use the shadow frame model instead of xFP */
/* single-purpose registers, given names for clarity */
#define xPC x20
@@ -114,14 +117,6 @@ codes.
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
diff --git a/runtime/interpreter/mterp/arm64/invoke.S b/runtime/interpreter/mterp/arm64/invoke.S
index ff1974c51d..7a32df7bca 100644
--- a/runtime/interpreter/mterp/arm64/invoke.S
+++ b/runtime/interpreter/mterp/arm64/invoke.S
@@ -9,11 +9,12 @@
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl $helper
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
diff --git a/runtime/interpreter/mterp/arm64/op_goto.S b/runtime/interpreter/mterp/arm64/op_goto.S
index db98a45fae..7e2f6a9c11 100644
--- a/runtime/interpreter/mterp/arm64/op_goto.S
+++ b/runtime/interpreter/mterp/arm64/op_goto.S
@@ -6,23 +6,20 @@
*/
/* goto +AA */
/* tuning: use sbfx for 6t2+ targets */
-#if MTERP_SUSPEND
- mov w0, wINST, lsl #16 // w0<- AAxx0000
- movs w1, w0, asr #24 // w1<- ssssssAA (sign-extended)
- add w2, w1, w1 // w2<- byte offset, set flags
- // If backwards branch refresh rIBASE
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#else
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue
lsl w0, wINST, #16 // w0<- AAxx0000
- asr w0, w0, #24 // w0<- ssssssAA (sign-extended)
- adds w1, w0, w0 // Convert dalvik offset to byte offset, setting flags
+ asr wINST, w0, #24 // wINST<- ssssssAA (sign-extended)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue
+ adds w1, wINST, wINST // Convert dalvik offset to byte offset, setting flags
FETCH_ADVANCE_INST_RB w1 // load wINST and advance xPC
// If backwards branch refresh rIBASE
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm64/op_goto_16.S b/runtime/interpreter/mterp/arm64/op_goto_16.S
index ff66a23c4e..b2b9924409 100644
--- a/runtime/interpreter/mterp/arm64/op_goto_16.S
+++ b/runtime/interpreter/mterp/arm64/op_goto_16.S
@@ -5,19 +5,18 @@
* double to get a byte offset.
*/
/* goto/16 +AAAA */
-#if MTERP_SUSPEND
- FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended)
- adds w1, w0, w0 // w1<- byte offset, flags set
- FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST
- ldrmi xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- GET_INST_OPCODE ip // extract opcode from rINST
- GOTO_OPCODE ip // jump to next instruction
-#else
- FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended)
+ FETCH_S wINST, 1 // wINST<- ssssAAAA (sign-extended)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w1, w0, w0 // w1<- byte offset, flags set
+ adds w1, wINST, wINST // w1<- byte offset, flags set
FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from rINST
GOTO_OPCODE ip // jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm64/op_goto_32.S b/runtime/interpreter/mterp/arm64/op_goto_32.S
index 8a6980ecea..b785857b9b 100644
--- a/runtime/interpreter/mterp/arm64/op_goto_32.S
+++ b/runtime/interpreter/mterp/arm64/op_goto_32.S
@@ -10,23 +10,20 @@
* offset to byte offset.
*/
/* goto/32 +AAAAAAAA */
-#if MTERP_SUSPEND
- FETCH w0, 1 // w0<- aaaa (lo)
- FETCH w1, 2 // w1<- AAAA (hi)
- orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa
- adds w1, w0, w0 // w1<- byte offset
- FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST
- ldrle xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- GET_INST_OPCODE ip // extract opcode from xINST
- GOTO_OPCODE ip // jump to next instruction
-#else
FETCH w0, 1 // w0<- aaaa (lo)
FETCH w1, 2 // w1<- AAAA (hi)
+ orr wINST, w0, w1, lsl #16 // wINST<- AAAAaaaa
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa
- adds w1, w0, w0 // w1<- byte offset
+ adds w1, wINST, wINST // w1<- byte offset
FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST
b.le MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from xINST
GOTO_OPCODE ip // jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm64/op_iget.S b/runtime/interpreter/mterp/arm64/op_iget.S
index 165c7308e1..88533bd33d 100644
--- a/runtime/interpreter/mterp/arm64/op_iget.S
+++ b/runtime/interpreter/mterp/arm64/op_iget.S
@@ -1,4 +1,4 @@
-%default { "is_object":"0", "helper":"artGet32InstanceFromCode"}
+%default { "extend":"", "is_object":"0", "helper":"artGet32InstanceFromCode"}
/*
* General instance field get.
*
@@ -12,6 +12,7 @@
mov x3, xSELF // w3<- self
bl $helper
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+ $extend
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
diff --git a/runtime/interpreter/mterp/arm64/op_packed_switch.S b/runtime/interpreter/mterp/arm64/op_packed_switch.S
index f087d23c0a..e8b4f04dfe 100644
--- a/runtime/interpreter/mterp/arm64/op_packed_switch.S
+++ b/runtime/interpreter/mterp/arm64/op_packed_switch.S
@@ -9,20 +9,6 @@
* for: packed-switch, sparse-switch
*/
/* op vAA, +BBBB */
-#if MTERP_SUSPEND
- FETCH w0, 1 // w0<- bbbb (lo)
- FETCH w1, 2 // w1<- BBBB (hi)
- mov w3, wINST, lsr #8 // w3<- AA
- orr w0, w0, w1, lsl #16 // w0<- BBBBbbbb
- GET_VREG w1, w3 // w1<- vAA
- add w0, rPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2
- bl $func // w0<- code-unit branch offset
- adds w1, w0, w0 // w1<- byte offset; clear V
- ldrle rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#else
FETCH w0, 1 // w0<- bbbb (lo)
FETCH w1, 2 // w1<- BBBB (hi)
lsr w3, wINST, #8 // w3<- AA
@@ -30,10 +16,18 @@
GET_VREG w1, w3 // w1<- vAA
add x0, xPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2
bl $func // w0<- code-unit branch offset
+ sbfm xINST, x0, 0, 31
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ mov x2, xINST
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w1, w0, w0 // w1<- byte offset; clear V
+ adds w1, wINST, wINST // w1<- byte offset; clear V
FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
b.le MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#endif
diff --git a/runtime/interpreter/mterp/arm64/zcmp.S b/runtime/interpreter/mterp/arm64/zcmp.S
index d4856d2668..e528d9f030 100644
--- a/runtime/interpreter/mterp/arm64/zcmp.S
+++ b/runtime/interpreter/mterp/arm64/zcmp.S
@@ -6,26 +6,37 @@
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- mov${condition} w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.${condition} .L_${opcode}_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_${opcode}_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, ${condition} // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, ${condition} // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 0afd2765db..b443c69718 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -20,6 +20,8 @@
#include "interpreter/interpreter_common.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "mterp.h"
+#include "jit/jit.h"
+#include "debugger.h"
namespace art {
namespace interpreter {
@@ -45,7 +47,9 @@ void CheckMterpAsmConstants() {
void InitMterpTls(Thread* self) {
self->SetMterpDefaultIBase(artMterpAsmInstructionStart);
self->SetMterpAltIBase(artMterpAsmAltInstructionStart);
- self->SetMterpCurrentIBase(artMterpAsmInstructionStart);
+ self->SetMterpCurrentIBase(TraceExecutionEnabled() ?
+ artMterpAsmAltInstructionStart :
+ artMterpAsmInstructionStart);
}
/*
@@ -139,6 +143,20 @@ extern "C" int32_t MterpDoPackedSwitch(const uint16_t* switchData, int32_t testV
return entries[index];
}
+extern "C" bool MterpShouldSwitchInterpreters()
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ const instrumentation::Instrumentation* const instrumentation =
+ Runtime::Current()->GetInstrumentation();
+ bool unhandled_instrumentation;
+ // TODO: enable for other targets after more extensive testing.
+ if ((kRuntimeISA == kArm64) || (kRuntimeISA == kArm) || (kRuntimeISA == kX86)) {
+ unhandled_instrumentation = instrumentation->NonJitProfilingActive();
+ } else {
+ unhandled_instrumentation = instrumentation->IsActive();
+ }
+ return unhandled_instrumentation || Dbg::IsDebuggerActive();
+}
+
extern "C" bool MterpInvokeVirtual(Thread* self, ShadowFrame* shadow_frame,
uint16_t* dex_pc_ptr, uint16_t inst_data )
@@ -429,6 +447,7 @@ extern "C" void MterpCheckBefore(Thread* self, ShadowFrame* shadow_frame)
} else {
self->AssertNoPendingException();
}
+ TraceExecution(*shadow_frame, inst, shadow_frame->GetDexPC());
}
extern "C" void MterpLogDivideByZeroException(Thread* self, ShadowFrame* shadow_frame)
@@ -488,6 +507,14 @@ extern "C" void MterpLogFallback(Thread* self, ShadowFrame* shadow_frame)
<< self->IsExceptionPending();
}
+extern "C" void MterpLogOSR(Thread* self, ShadowFrame* shadow_frame, int32_t offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ UNUSED(self);
+ const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr());
+ uint16_t inst_data = inst->Fetch16(0);
+ LOG(INFO) << "OSR: " << inst->Opcode(inst_data) << ", offset = " << offset;
+}
+
extern "C" void MterpLogSuspendFallback(Thread* self, ShadowFrame* shadow_frame, uint32_t flags)
SHARED_REQUIRES(Locks::mutator_lock_) {
UNUSED(self);
@@ -500,9 +527,10 @@ extern "C" void MterpLogSuspendFallback(Thread* self, ShadowFrame* shadow_frame,
}
}
-extern "C" void MterpSuspendCheck(Thread* self)
+extern "C" bool MterpSuspendCheck(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
self->AllowThreadSuspension();
+ return MterpShouldSwitchInterpreters();
}
extern "C" int artSet64IndirectStaticFromMterp(uint32_t field_idx, ArtMethod* referrer,
@@ -618,5 +646,15 @@ extern "C" mirror::Object* artIGetObjectFromMterp(mirror::Object* obj, uint32_t
return obj->GetFieldObject<mirror::Object>(MemberOffset(field_offset));
}
+extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int32_t offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ArtMethod* method = shadow_frame->GetMethod();
+ JValue* result = shadow_frame->GetResultRegister();
+ uint32_t dex_pc = shadow_frame->GetDexPC();
+ const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
+ instrumentation->Branch(self, method, dex_pc, offset);
+ return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result);
+}
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/mterp/mterp.h b/runtime/interpreter/mterp/mterp.h
index 90d21e9c89..8d24641ce4 100644
--- a/runtime/interpreter/mterp/mterp.h
+++ b/runtime/interpreter/mterp/mterp.h
@@ -30,6 +30,7 @@ namespace interpreter {
void InitMterpTls(Thread* self);
void CheckMterpAsmConstants();
+extern "C" bool MterpShouldSwitchInterpreters();
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index ee195598db..94cbd2d10e 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -92,6 +92,9 @@ unspecified registers or condition codes.
*/
#include "asm_support.h"
+#define MTERP_PROFILE_BRANCHES 1
+#define MTERP_LOGGING 0
+
/* During bringup, we'll use the shadow frame model instead of rFP */
/* single-purpose registers, given names for clarity */
#define rPC r4
@@ -116,14 +119,6 @@ unspecified registers or condition codes.
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -1111,20 +1106,28 @@ artMterpAsmInstructionStart = .L_op_nop
*/
/* goto +AA */
/* tuning: use sbfx for 6t2+ targets */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsl #16 @ r0<- AAxx0000
- movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended)
- add r2, r1, r1 @ r2<- byte offset, set flags
- @ If backwards branch refresh rIBASE
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended)
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r2, rINST, rINST @ r2<- byte offset, set flags
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
+ @ If backwards branch refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
- ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
mov r0, rINST, lsl #16 @ r0<- AAxx0000
- movs r1, r0, asr #24 @ r1<- ssssssAA (sign-extended)
- add r2, r1, r1 @ r2<- byte offset, set flags
+ movs rINST, r0, asr #24 @ rINST<- ssssssAA (sign-extended)
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r2, rINST, rINST @ r2<- byte offset, set flags
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
@ If backwards branch refresh rIBASE
bmi MterpCheckSuspendAndContinue
@@ -1143,17 +1146,25 @@ artMterpAsmInstructionStart = .L_op_nop
* double to get a byte offset.
*/
/* goto/16 +AAAA */
-#if MTERP_SUSPEND
- FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended)
- adds r1, r0, r0 @ r1<- byte offset, flags set
+#if MTERP_PROFILE_BRANCHES
+ FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended)
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset, flags set
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
- FETCH_S r0, 1 @ r0<- ssssAAAA (sign-extended)
+ FETCH_S rINST, 1 @ rINST<- ssssAAAA (sign-extended)
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- adds r1, r0, r0 @ r1<- byte offset, flags set
+ adds r1, rINST, rINST @ r1<- byte offset, flags set
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1176,21 +1187,29 @@ artMterpAsmInstructionStart = .L_op_nop
* offset to byte offset.
*/
/* goto/32 +AAAAAAAA */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
FETCH r0, 1 @ r0<- aaaa (lo)
FETCH r1, 2 @ r1<- AAAA (hi)
- orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa
- adds r1, r0, r0 @ r1<- byte offset
+ orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
FETCH r0, 1 @ r0<- aaaa (lo)
FETCH r1, 2 @ r1<- AAAA (hi)
+ orr rINST, r0, r1, lsl #16 @ rINST<- AAAAaaaa
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- orr r0, r0, r1, lsl #16 @ r0<- AAAAaaaa
- adds r1, r0, r0 @ r1<- byte offset
+ adds r1, rINST, rINST @ r1<- byte offset
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1211,7 +1230,7 @@ artMterpAsmInstructionStart = .L_op_nop
* for: packed-switch, sparse-switch
*/
/* op vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
FETCH r0, 1 @ r0<- bbbb (lo)
FETCH r1, 2 @ r1<- BBBB (hi)
mov r3, rINST, lsr #8 @ r3<- AA
@@ -1219,9 +1238,18 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r1, r3 @ r1<- vAA
add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
bl MterpDoPackedSwitch @ r0<- code-unit branch offset
- adds r1, r0, r0 @ r1<- byte offset; clear V
- ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ mov rINST, r0
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset; clear V
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
+ ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1232,8 +1260,9 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r1, r3 @ r1<- vAA
add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
bl MterpDoPackedSwitch @ r0<- code-unit branch offset
+ mov rINST, r0
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- adds r1, r0, r0 @ r1<- byte offset; clear V
+ adds r1, rINST, rINST @ r1<- byte offset; clear V
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1255,7 +1284,7 @@ artMterpAsmInstructionStart = .L_op_nop
* for: packed-switch, sparse-switch
*/
/* op vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
FETCH r0, 1 @ r0<- bbbb (lo)
FETCH r1, 2 @ r1<- BBBB (hi)
mov r3, rINST, lsr #8 @ r3<- AA
@@ -1263,9 +1292,18 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r1, r3 @ r1<- vAA
add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
bl MterpDoSparseSwitch @ r0<- code-unit branch offset
- adds r1, r0, r0 @ r1<- byte offset; clear V
- ldrle rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh handler base
+ mov rINST, r0
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
+ adds r1, rINST, rINST @ r1<- byte offset; clear V
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
+ ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1276,8 +1314,9 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r1, r3 @ r1<- vAA
add r0, rPC, r0, lsl #1 @ r0<- PC + BBBBbbbb*2
bl MterpDoSparseSwitch @ r0<- code-unit branch offset
+ mov rINST, r0
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- adds r1, r0, r0 @ r1<- byte offset; clear V
+ adds r1, rINST, rINST @ r1<- byte offset; clear V
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
ble MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1495,17 +1534,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movne r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ bne .L_op_if_eq_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_eq_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1514,10 +1565,10 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movne r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ movne rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1538,17 +1589,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- moveq r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ beq .L_op_if_ne_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_ne_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1557,10 +1620,10 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- moveq r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ moveq rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1581,17 +1644,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movge r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ bge .L_op_if_lt_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_lt_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1600,10 +1675,10 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movge r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ movge rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1624,17 +1699,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movlt r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ blt .L_op_if_ge_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_ge_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1643,10 +1730,10 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movlt r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ movlt rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1667,17 +1754,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movle r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ ble .L_op_if_gt_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_gt_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1686,10 +1785,10 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movle r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ movle rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1710,17 +1809,29 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r1, rINST, lsr #12 @ r1<- B
ubfx r0, rINST, #8, #4 @ r0<- A
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movgt r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ bgt .L_op_if_le_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r2, rINST, rINST @ convert to bytes, check sign
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_le_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
@@ -1729,10 +1840,10 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG r3, r1 @ r3<- vB
GET_VREG r2, r0 @ r2<- vA
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
cmp r2, r3 @ compare (vA, vB)
- movgt r1, #2 @ r1<- BYTE branch dist for not-taken
- adds r2, r1, r1 @ convert to bytes, check sign
+ movgt rINST, #2 @ rINST<- BYTE branch dist for not-taken
+ adds r2, rINST, rINST @ convert to bytes, check sign
FETCH_ADVANCE_INST_RB r2 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1753,25 +1864,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movne r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ bne .L_op_if_eqz_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_eqz_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movne r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ movne rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1792,25 +1915,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- moveq r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ beq .L_op_if_nez_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_nez_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- moveq r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ moveq rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1831,25 +1966,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movge r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ bge .L_op_if_ltz_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_ltz_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movge r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ movge rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1870,25 +2017,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movlt r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ blt .L_op_if_gez_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_gez_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movlt r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ movlt rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1909,25 +2068,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movle r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ ble .L_op_if_gtz_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_gtz_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movle r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ movle rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -1948,25 +2119,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
+#if MTERP_PROFILE_BRANCHES
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
+ ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movgt r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ bgt .L_op_if_lez_not_taken
+ EXPORT_PC
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpProfileBranch @ (self, shadow_frame, offset)
+ cmp r0, #0
+ bne MterpOnStackReplacement @ Note: offset must be in rINST
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
- ldrmi rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh table base
+ bmi MterpCheckSuspendAndContinue
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+.L_op_if_lez_not_taken:
+ FETCH_ADVANCE_INST 2 @ update rPC, load rINST
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
#else
mov r0, rINST, lsr #8 @ r0<- AA
GET_VREG r2, r0 @ r2<- vAA
- FETCH_S r1, 1 @ r1<- branch offset, in code units
+ FETCH_S rINST, 1 @ rINST<- branch offset, in code units
ldr lr, [rSELF, #THREAD_FLAGS_OFFSET]
cmp r2, #0 @ compare (vA, 0)
- movgt r1, #2 @ r1<- inst branch dist for not-taken
- adds r1, r1, r1 @ convert to bytes & set flags
+ movgt rINST, #2 @ rINST<- inst branch dist for not-taken
+ adds r1, rINST, rINST @ convert to bytes & set flags
FETCH_ADVANCE_INST_RB r1 @ update rPC, load rINST
bmi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -3294,6 +3477,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3326,6 +3512,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3358,6 +3547,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3383,6 +3575,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3409,6 +3604,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3453,6 +3651,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3478,6 +3679,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3503,6 +3707,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3528,6 +3735,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3553,6 +3763,9 @@ artMterpAsmInstructionStart = .L_op_nop
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -4948,16 +5161,16 @@ constvalop_long_to_double:
add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
GET_VREG r2, r0 @ r2<- vCC
ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
and r2, r2, #63 @ r2<- r2 & 0x3f
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
-
- mov r1, r1, asl r2 @ r1<- r1 << r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
- movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- mov r0, r0, asl r2 @ r0<- r0 << r2
+ mov r0, r0, asl r2 @ r0<- r0 << r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
@@ -4980,16 +5193,16 @@ constvalop_long_to_double:
add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
GET_VREG r2, r0 @ r2<- vCC
ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
and r2, r2, #63 @ r0<- r0 & 0x3f
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
- movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- mov r1, r1, asr r2 @ r1<- r1 >> r2
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
@@ -5012,16 +5225,16 @@ constvalop_long_to_double:
add r3, rFP, r3, lsl #2 @ r3<- &fp[BB]
GET_VREG r2, r0 @ r2<- vCC
ldmia r3, {r0-r1} @ r0/r1<- vBB/vBB+1
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
and r2, r2, #63 @ r0<- r0 & 0x3f
add r9, rFP, r9, lsl #2 @ r9<- &fp[AA]
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
- movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
- mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
@@ -6087,17 +6300,17 @@ constvalop_long_to_double:
mov r3, rINST, lsr #12 @ r3<- B
ubfx r9, rINST, #8, #4 @ r9<- A
GET_VREG r2, r3 @ r2<- vB
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
and r2, r2, #63 @ r2<- r2 & 0x3f
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
-
- mov r1, r1, asl r2 @ r1<- r1 << r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
+ mov r1, r1, asl r2 @ r1<- r1 << r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r1, r1, r0, lsr r3 @ r1<- r1 | (r0 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
- movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
- mov r0, r0, asl r2 @ r0<- r0 << r2
+ movpl r1, r0, asl ip @ if r2 >= 32, r1<- r0 << (r2-32)
+ mov r0, r0, asl r2 @ r0<- r0 << r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
@@ -6114,17 +6327,17 @@ constvalop_long_to_double:
mov r3, rINST, lsr #12 @ r3<- B
ubfx r9, rINST, #8, #4 @ r9<- A
GET_VREG r2, r3 @ r2<- vB
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
and r2, r2, #63 @ r2<- r2 & 0x3f
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
- movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
- mov r1, r1, asr r2 @ r1<- r1 >> r2
+ movpl r0, r1, asr ip @ if r2 >= 32, r0<-r1 >> (r2-32)
+ mov r1, r1, asr r2 @ r1<- r1 >> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
@@ -6141,17 +6354,17 @@ constvalop_long_to_double:
mov r3, rINST, lsr #12 @ r3<- B
ubfx r9, rINST, #8, #4 @ r9<- A
GET_VREG r2, r3 @ r2<- vB
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
add r9, rFP, r9, lsl #2 @ r9<- &fp[A]
and r2, r2, #63 @ r2<- r2 & 0x3f
ldmia r9, {r0-r1} @ r0/r1<- vAA/vAA+1
-
- mov r0, r0, lsr r2 @ r0<- r2 >> r2
- rsb r3, r2, #32 @ r3<- 32 - r2
- orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
- subs ip, r2, #32 @ ip<- r2 - 32
+ mov r0, r0, lsr r2 @ r0<- r2 >> r2
+ rsb r3, r2, #32 @ r3<- 32 - r2
+ orr r0, r0, r1, asl r3 @ r0<- r0 | (r1 << (32-r2))
+ subs ip, r2, #32 @ ip<- r2 - 32
FETCH_ADVANCE_INST 1 @ advance rPC, load rINST
- movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
- mov r1, r1, lsr r2 @ r1<- r1 >>> r2
+ movpl r0, r1, lsr ip @ if r2 >= 32, r0<-r1 >>> (r2-32)
+ mov r1, r1, lsr r2 @ r1<- r1 >>> r2
GET_INST_OPCODE ip @ extract opcode from rINST
stmia r9, {r0-r1} @ vAA/vAA+1<- r0/r1
GOTO_OPCODE ip @ jump to next instruction
@@ -7284,6 +7497,9 @@ constvalop_long_to_double:
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -7309,6 +7525,9 @@ constvalop_long_to_double:
cmp r0, #0
beq MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -12098,7 +12317,6 @@ artMterpAsmAltInstructionEnd:
* has not yet been thrown. Just bail out to the reference interpreter to deal with it.
* TUNING: for consistency, we may want to just go ahead and handle these here.
*/
-#define MTERP_LOGGING 0
common_errDivideByZero:
EXPORT_PC
#if MTERP_LOGGING
@@ -12189,8 +12407,12 @@ MterpException:
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
add rPC, r0, #CODEITEM_INSNS_OFFSET
add rPC, rPC, r1, lsl #1 @ generate new dex_pc_ptr
- str rPC, [rFP, #OFF_FP_DEX_PC_PTR]
+ /* Do we need to switch interpreters? */
+ bl MterpShouldSwitchInterpreters
+ cmp r0, #0
+ bne MterpFallback
/* resume execution at catch block */
+ EXPORT_PC
FETCH_INST
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -12202,14 +12424,33 @@ MterpException:
*/
MterpCheckSuspendAndContinue:
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET] @ refresh rIBASE
+ ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST)
+ bne 1f
+ GET_INST_OPCODE ip @ extract opcode from rINST
+ GOTO_OPCODE ip @ jump to next instruction
+1:
EXPORT_PC
mov r0, rSELF
- ands lr, #(THREAD_SUSPEND_REQUEST | THREAD_CHECKPOINT_REQUEST)
- blne MterpSuspendCheck @ (self)
+ bl MterpSuspendCheck @ (self)
+ cmp r0, #0
+ bne MterpFallback
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ mov r0, rSELF
+ add r1, rFP, #OFF_FP_SHADOWFRAME
+ mov r2, rINST
+ bl MterpLogOSR
+#endif
+ mov r0, #1 @ Signal normal return
+ b MterpDone
+
+/*
* Bail out to reference interpreter.
*/
MterpFallback:
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index e9d28abf8b..e4825f0489 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -94,6 +94,9 @@ codes.
*/
#include "asm_support.h"
+#define MTERP_PROFILE_BRANCHES 1
+#define MTERP_LOGGING 0
+
/* During bringup, we'll use the shadow frame model instead of xFP */
/* single-purpose registers, given names for clarity */
#define xPC x20
@@ -121,14 +124,6 @@ codes.
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
- */
-#define MTERP_SUSPEND 0
-
-/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
* be done *before* something throws.
*
@@ -1087,26 +1082,23 @@ artMterpAsmInstructionStart = .L_op_nop
*/
/* goto +AA */
/* tuning: use sbfx for 6t2+ targets */
-#if MTERP_SUSPEND
- mov w0, wINST, lsl #16 // w0<- AAxx0000
- movs w1, w0, asr #24 // w1<- ssssssAA (sign-extended)
- add w2, w1, w1 // w2<- byte offset, set flags
- // If backwards branch refresh rIBASE
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#else
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue
lsl w0, wINST, #16 // w0<- AAxx0000
- asr w0, w0, #24 // w0<- ssssssAA (sign-extended)
- adds w1, w0, w0 // Convert dalvik offset to byte offset, setting flags
+ asr wINST, w0, #24 // wINST<- ssssssAA (sign-extended)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+#endif
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET] // Preload flags for MterpCheckSuspendAndContinue
+ adds w1, wINST, wINST // Convert dalvik offset to byte offset, setting flags
FETCH_ADVANCE_INST_RB w1 // load wINST and advance xPC
// If backwards branch refresh rIBASE
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
.balign 128
@@ -1119,22 +1111,21 @@ artMterpAsmInstructionStart = .L_op_nop
* double to get a byte offset.
*/
/* goto/16 +AAAA */
-#if MTERP_SUSPEND
- FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended)
- adds w1, w0, w0 // w1<- byte offset, flags set
- FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST
- ldrmi xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- GET_INST_OPCODE ip // extract opcode from rINST
- GOTO_OPCODE ip // jump to next instruction
-#else
- FETCH_S w0, 1 // w0<- ssssAAAA (sign-extended)
+ FETCH_S wINST, 1 // wINST<- ssssAAAA (sign-extended)
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w1, w0, w0 // w1<- byte offset, flags set
+ adds w1, wINST, wINST // w1<- byte offset, flags set
FETCH_ADVANCE_INST_RB w1 // update rPC, load rINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from rINST
GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
.balign 128
@@ -1152,26 +1143,23 @@ artMterpAsmInstructionStart = .L_op_nop
* offset to byte offset.
*/
/* goto/32 +AAAAAAAA */
-#if MTERP_SUSPEND
- FETCH w0, 1 // w0<- aaaa (lo)
- FETCH w1, 2 // w1<- AAAA (hi)
- orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa
- adds w1, w0, w0 // w1<- byte offset
- FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST
- ldrle xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- GET_INST_OPCODE ip // extract opcode from xINST
- GOTO_OPCODE ip // jump to next instruction
-#else
FETCH w0, 1 // w0<- aaaa (lo)
FETCH w1, 2 // w1<- AAAA (hi)
+ orr wINST, w0, w1, lsl #16 // wINST<- AAAAaaaa
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- orr w0, w0, w1, lsl #16 // w0<- AAAAaaaa
- adds w1, w0, w0 // w1<- byte offset
+ adds w1, wINST, wINST // w1<- byte offset
FETCH_ADVANCE_INST_RB w1 // update rPC, load xINST
b.le MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from xINST
GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
.balign 128
@@ -1187,20 +1175,6 @@ artMterpAsmInstructionStart = .L_op_nop
* for: packed-switch, sparse-switch
*/
/* op vAA, +BBBB */
-#if MTERP_SUSPEND
- FETCH w0, 1 // w0<- bbbb (lo)
- FETCH w1, 2 // w1<- BBBB (hi)
- mov w3, wINST, lsr #8 // w3<- AA
- orr w0, w0, w1, lsl #16 // w0<- BBBBbbbb
- GET_VREG w1, w3 // w1<- vAA
- add w0, rPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2
- bl MterpDoPackedSwitch // w0<- code-unit branch offset
- adds w1, w0, w0 // w1<- byte offset; clear V
- ldrle rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#else
FETCH w0, 1 // w0<- bbbb (lo)
FETCH w1, 2 // w1<- BBBB (hi)
lsr w3, wINST, #8 // w3<- AA
@@ -1208,13 +1182,21 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w1, w3 // w1<- vAA
add x0, xPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2
bl MterpDoPackedSwitch // w0<- code-unit branch offset
+ sbfm xINST, x0, 0, 31
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ mov x2, xINST
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w1, w0, w0 // w1<- byte offset; clear V
+ adds w1, wINST, wINST // w1<- byte offset; clear V
FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
b.le MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
.balign 128
@@ -1231,20 +1213,6 @@ artMterpAsmInstructionStart = .L_op_nop
* for: packed-switch, sparse-switch
*/
/* op vAA, +BBBB */
-#if MTERP_SUSPEND
- FETCH w0, 1 // w0<- bbbb (lo)
- FETCH w1, 2 // w1<- BBBB (hi)
- mov w3, wINST, lsr #8 // w3<- AA
- orr w0, w0, w1, lsl #16 // w0<- BBBBbbbb
- GET_VREG w1, w3 // w1<- vAA
- add w0, rPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2
- bl MterpDoSparseSwitch // w0<- code-unit branch offset
- adds w1, w0, w0 // w1<- byte offset; clear V
- ldrle rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh handler base
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- GET_INST_OPCODE ip // extract opcode from wINST
- GOTO_OPCODE ip // jump to next instruction
-#else
FETCH w0, 1 // w0<- bbbb (lo)
FETCH w1, 2 // w1<- BBBB (hi)
lsr w3, wINST, #8 // w3<- AA
@@ -1252,13 +1220,21 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w1, w3 // w1<- vAA
add x0, xPC, w0, lsl #1 // w0<- PC + BBBBbbbb*2
bl MterpDoSparseSwitch // w0<- code-unit branch offset
+ sbfm xINST, x0, 0, 31
+#if MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ mov x2, xINST
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement
+#endif
ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
- adds w1, w0, w0 // w1<- byte offset; clear V
+ adds w1, wINST, wINST // w1<- byte offset; clear V
FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
b.le MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
-#endif
/* ------------------------------ */
@@ -1396,17 +1372,28 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- moveq w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.eq .L_op_if_eq_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_eq_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -1415,11 +1402,11 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, eq // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1440,17 +1427,28 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- movne w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.ne .L_op_if_ne_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_ne_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -1459,11 +1457,11 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, ne // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1484,17 +1482,28 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- movlt w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.lt .L_op_if_lt_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_lt_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -1503,11 +1512,11 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, lt // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1528,17 +1537,28 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- movge w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.ge .L_op_if_ge_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_ge_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -1547,11 +1567,11 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, ge // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1572,17 +1592,28 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- movgt w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.gt .L_op_if_gt_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_gt_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -1591,11 +1622,11 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, gt // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1616,17 +1647,28 @@ artMterpAsmInstructionStart = .L_op_nop
* For: if-eq, if-ne, if-lt, if-ge, if-gt, if-le
*/
/* if-cmp vA, vB, +CCCC */
-#if MTERP_SUSPEND
- mov w1, wINST, lsr #12 // w1<- B
+#if MTERP_PROFILE_BRANCHES
+ lsr w1, wINST, #12 // w1<- B
ubfx w0, wINST, #8, #4 // w0<- A
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // wINST<- branch offset, in code units
cmp w2, w3 // compare (vA, vB)
- movle w1, #2 // w1<- BYTE branch dist for not-taken
- adds w2, w1, w1 // convert to bytes, check sign
+ b.le .L_op_if_le_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_le_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31 // Sign extend branch offset
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in xINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh rIBASE
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
@@ -1635,11 +1677,11 @@ artMterpAsmInstructionStart = .L_op_nop
GET_VREG w3, w1 // w3<- vB
GET_VREG w2, w0 // w2<- vA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Offset if branch not taken
cmp w2, w3 // compare (vA, vB)
- csel w1, w1, w0, le // Branch if true
- adds w2, w1, w1 // convert to bytes, check sign
+ csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg.
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes, check sign
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1660,26 +1702,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- moveq w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.eq .L_op_if_eqz_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_eqz_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, eq // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, eq // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1700,26 +1753,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- movne w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.ne .L_op_if_nez_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_nez_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, ne // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, ne // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1740,26 +1804,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- movlt w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.lt .L_op_if_ltz_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_ltz_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, lt // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, lt // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1780,26 +1855,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- movge w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.ge .L_op_if_gez_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_gez_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, ge // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, ge // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1820,26 +1906,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- movgt w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.gt .L_op_if_gtz_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_gtz_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, gt // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, gt // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -1860,26 +1957,37 @@ artMterpAsmInstructionStart = .L_op_nop
* for: if-eqz, if-nez, if-ltz, if-gez, if-gtz, if-lez
*/
/* if-cmp vAA, +BBBB */
-#if MTERP_SUSPEND
- mov w0, wINST, lsr #8 // w0<- AA
+#if MTERP_PROFILE_BRANCHES
+ lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
- FETCH_S w1, 1 // w1<- branch offset, in code units
+ FETCH_S wINST, 1 // w1<- branch offset, in code units
cmp w2, #0 // compare (vA, 0)
- movle w1, #2 // w1<- inst branch dist for not-taken
- adds w1, w1, w1 // convert to bytes & set flags
- FETCH_ADVANCE_INST_RB w1 // update rPC, load wINST
- ldrmi rIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET] // refresh table base
+ b.le .L_op_if_lez_taken
+ FETCH_ADVANCE_INST 2 // update rPC, load wINST
+ GET_INST_OPCODE ip // extract opcode from wINST
+ GOTO_OPCODE ip // jump to next instruction
+.L_op_if_lez_taken:
+ EXPORT_PC
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpProfileBranch // (self, shadow_frame, offset)
+ cbnz w0, MterpOnStackReplacement // Note: offset must be in wINST
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
+ FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
+ b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
#else
lsr w0, wINST, #8 // w0<- AA
GET_VREG w2, w0 // w2<- vAA
FETCH_S w1, 1 // w1<- branch offset, in code units
- ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
mov w0, #2 // Branch offset if not taken
cmp w2, #0 // compare (vA, 0)
- csel w1, w1, w0, le // Branch if true
- adds w2, w1, w1 // convert to bytes & set flags
+ csel wINST, w1, w0, le // Branch if true, stashing result in callee save reg
+ ldr w7, [xSELF, #THREAD_FLAGS_OFFSET]
+ adds w2, wINST, wINST // convert to bytes & set flags
FETCH_ADVANCE_INST_RB w2 // update rPC, load wINST
b.mi MterpCheckSuspendAndContinue
GET_INST_OPCODE ip // extract opcode from wINST
@@ -2401,6 +2509,7 @@ artMterpAsmInstructionStart = .L_op_nop
mov x3, xSELF // w3<- self
bl artGet32InstanceFromCode
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
@@ -2457,6 +2566,7 @@ artMterpAsmInstructionStart = .L_op_nop
mov x3, xSELF // w3<- self
bl artGetObjInstanceFromCode
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
@@ -2488,6 +2598,7 @@ artMterpAsmInstructionStart = .L_op_nop
mov x3, xSELF // w3<- self
bl artGetBooleanInstanceFromCode
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+ uxtb w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
@@ -2519,6 +2630,7 @@ artMterpAsmInstructionStart = .L_op_nop
mov x3, xSELF // w3<- self
bl artGetByteInstanceFromCode
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+ sxtb w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
@@ -2550,6 +2662,7 @@ artMterpAsmInstructionStart = .L_op_nop
mov x3, xSELF // w3<- self
bl artGetCharInstanceFromCode
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+ uxth w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
@@ -2581,6 +2694,7 @@ artMterpAsmInstructionStart = .L_op_nop
mov x3, xSELF // w3<- self
bl artGetShortInstanceFromCode
ldr x3, [xSELF, #THREAD_EXCEPTION_OFFSET]
+ sxth w0, w0
ubfx w2, wINST, #8, #4 // w2<- A
PREFETCH_INST 2
cbnz x3, MterpPossibleException // bail out
@@ -3158,11 +3272,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeVirtual
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3190,11 +3305,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeSuper
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3222,11 +3338,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeDirect
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3247,11 +3364,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeStatic
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3273,11 +3391,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeInterface
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3320,11 +3439,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeVirtualRange
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3345,11 +3465,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeSuperRange
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3370,11 +3491,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeDirectRange
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3395,11 +3517,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeStaticRange
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -3420,11 +3543,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeInterfaceRange
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -6852,11 +6976,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeVirtualQuick
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -6877,11 +7002,12 @@ artMterpAsmInstructionStart = .L_op_nop
mov x0, xSELF
add x1, xFP, #OFF_FP_SHADOWFRAME
mov x2, xPC
- // and x3, xINST, 0xFFFF
mov x3, xINST
bl MterpInvokeVirtualQuickRange
cbz w0, MterpException
FETCH_ADVANCE_INST 3
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -11565,7 +11691,6 @@ artMterpAsmAltInstructionEnd:
* has not yet been thrown. Just bail out to the reference interpreter to deal with it.
* TUNING: for consistency, we may want to just go ahead and handle these here.
*/
-#define MTERP_LOGGING 0
common_errDivideByZero:
EXPORT_PC
#if MTERP_LOGGING
@@ -11654,8 +11779,11 @@ MterpException:
ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET]
add xPC, x0, #CODEITEM_INSNS_OFFSET
add xPC, xPC, x1, lsl #1 // generate new dex_pc_ptr
- str xPC, [xFP, #OFF_FP_DEX_PC_PTR]
+ /* Do we need to switch interpreters? */
+ bl MterpShouldSwitchInterpreters
+ cbnz w0, MterpFallback
/* resume execution at catch block */
+ EXPORT_PC
FETCH_INST
GET_INST_OPCODE ip
GOTO_OPCODE ip
@@ -11675,10 +11803,24 @@ check1:
EXPORT_PC
mov x0, xSELF
bl MterpSuspendCheck // (self)
+ cbnz x0, MterpFallback // Something in the environment changed, switch interpreters
GET_INST_OPCODE ip // extract opcode from wINST
GOTO_OPCODE ip // jump to next instruction
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ mov x0, xSELF
+ add x1, xFP, #OFF_FP_SHADOWFRAME
+ sbfm x2, xINST, 0, 31
+ bl MterpLogOSR
+#endif
+ mov x0, #1 // Signal normal return
+ b MterpDone
+
+/*
* Bail out to reference interpreter.
*/
MterpFallback:
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index 96229ceba0..b05360b6ae 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -163,13 +163,26 @@ unspecified registers or condition codes.
#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
+#define MTERP_PROFILE_BRANCHES 1
+#define MTERP_LOGGING 0
+
/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
+ * Profile branch. rINST should contain the offset. %eax is scratch.
*/
-#define MTERP_SUSPEND 0
+.macro MTERP_PROFILE_BRANCH
+#ifdef MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %eax
+ movl %eax, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpProfileBranch)
+ testb %al, %al
+ jnz MterpOnStackReplacement
+ RESTORE_IBASE
+#endif
+.endm
/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
@@ -189,13 +202,21 @@ unspecified registers or condition codes.
/*
* Refresh handler table.
+ */
+.macro REFRESH_IBASE
+ movl rSELF, rIBASE
+ movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
+.endm
+
+/*
+ * Refresh handler table.
* IBase handles uses the caller save register so we must restore it after each call.
* Also it is used as a result of some 64-bit operations (like imul) and we should
* restore it in such cases also.
*
* TODO: Consider spilling the IBase instead of restoring it from Thread structure.
*/
-.macro REFRESH_IBASE
+.macro RESTORE_IBASE
movl rSELF, rIBASE
movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
.endm
@@ -203,7 +224,7 @@ unspecified registers or condition codes.
/*
* If rSELF is already loaded then we can use it from known reg.
*/
-.macro REFRESH_IBASE_FROM_SELF _reg
+.macro RESTORE_IBASE_FROM_SELF _reg
movl THREAD_CURRENT_IBASE_OFFSET(\_reg), rIBASE
.endm
@@ -771,8 +792,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -790,8 +811,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -809,8 +830,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -828,8 +849,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
call SYMBOL(artLockObjectFromCode) # (object, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -851,8 +872,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
call SYMBOL(artUnlockObjectFromCode) # (object, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -874,8 +895,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpCheckCast) # (index, &obj, method, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -903,7 +924,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
andb $0xf, rINSTbl # rINSTbl <- A
@@ -943,8 +964,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 34
movl rINST, OUT_ARG2(%esp)
call SYMBOL(MterpNewInstance)
- REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ RESTORE_IBASE
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -969,8 +990,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpNewArray)
- REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ RESTORE_IBASE
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -994,7 +1015,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp)
call SYMBOL(MterpFilledNewArray)
REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -1019,7 +1040,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp)
call SYMBOL(MterpFilledNewArrayRange)
REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -1037,7 +1058,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG1(%esp)
call SYMBOL(MterpFillArrayData) # (obj, payload)
REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -1068,17 +1089,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
* double to get a byte offset.
*/
/* goto +AA */
- movsbl rINSTbl, %eax # eax <- ssssssAA
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ movsbl rINSTbl, rINST # rINST <- ssssssAA
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # rINST <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 1f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
/* ------------------------------ */
@@ -1092,17 +1108,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
* double to get a byte offset.
*/
/* goto/16 +AAAA */
- movswl 2(rPC), %eax # eax <- ssssAAAA
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ movswl 2(rPC), rINST # rINST <- ssssAAAA
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # rINST <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 1f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
/* ------------------------------ */
@@ -1121,17 +1132,12 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
* offset to byte offset.
*/
/* goto/32 +AAAAAAAA */
- movl 2(rPC), %eax # eax <- AAAAAAAA
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ movl 2(rPC), rINST # rINST <- AAAAAAAA
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # rINST <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 1f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
/* ------------------------------ */
@@ -1154,17 +1160,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
call SYMBOL(MterpDoPackedSwitch)
- addl %eax, %eax
- leal (rPC, %eax), rPC
+ movl %eax, rINST
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST
+ leal (rPC, rINST), rPC
FETCH_INST
REFRESH_IBASE
- jg 1f
-#if MTERP_SUSPEND
- # REFRESH_IBASE - we did it above.
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue
GOTO_NEXT
/* ------------------------------ */
@@ -1188,17 +1190,13 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
call SYMBOL(MterpDoSparseSwitch)
- addl %eax, %eax
- leal (rPC, %eax), rPC
+ movl %eax, rINST
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST
+ leal (rPC, rINST), rPC
FETCH_INST
REFRESH_IBASE
- jg 1f
-#if MTERP_SUSPEND
- # REFRESH_IBASE - we did it above.
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue
GOTO_NEXT
@@ -1416,20 +1414,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, %eax # assume not taken
+ movl $2, rINST
jne 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1451,20 +1444,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, %eax # assume not taken
+ movl $2, rINST
je 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1486,20 +1474,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, %eax # assume not taken
+ movl $2, rINST
jge 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1521,20 +1504,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, %eax # assume not taken
+ movl $2, rINST
jl 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1556,20 +1534,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, %eax # assume not taken
+ movl $2, rINST
jle 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1591,20 +1564,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
GET_VREG %eax, %ecx # eax <- vA
sarl $4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $2, %eax # assume not taken
+ movl $2, rINST
jg 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1622,20 +1590,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, %eax # assume branch not taken
+ movl $2, rINST
jne 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1653,20 +1616,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, %eax # assume branch not taken
+ movl $2, rINST
je 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1684,20 +1642,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, %eax # assume branch not taken
+ movl $2, rINST
jge 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1715,20 +1668,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, %eax # assume branch not taken
+ movl $2, rINST
jl 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1746,20 +1694,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, %eax # assume branch not taken
+ movl $2, rINST
jle 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1777,20 +1720,15 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
*/
/* if-cmp vAA, +BBBB */
cmpl $0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $2, %eax # assume branch not taken
+ movl $2, rINST
jg 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
@@ -1923,7 +1861,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG1(%esp)
call SYMBOL(artAGetObjectFromMterp) # (array, index)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
SET_VREG_OBJECT %eax, rINST
@@ -2090,8 +2028,8 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 77
movl rINST, OUT_ARG2(%esp)
call SYMBOL(MterpAputObject) # (array, index)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2221,7 +2159,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artGet32InstanceFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
@@ -2259,7 +2197,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
andb $0xf, rINSTbl # rINST <- A
SET_VREG %eax, rINST
SET_VREG_HIGH %edx, rINST
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2285,7 +2223,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artGetObjInstanceFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
@@ -2320,7 +2258,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artGetBooleanInstanceFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
@@ -2355,7 +2293,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artGetByteInstanceFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
@@ -2390,7 +2328,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artGetCharInstanceFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
@@ -2425,7 +2363,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artGetShortInstanceFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf, rINSTbl # rINST <- A
@@ -2461,9 +2399,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet32InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2485,9 +2423,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet64InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2503,9 +2441,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpIputObject)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2533,9 +2471,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet8InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2564,9 +2502,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet8InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2595,9 +2533,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet16InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2626,9 +2564,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet16InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2652,7 +2590,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL(artGet32StaticFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
@@ -2685,7 +2623,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
jnz MterpException
SET_VREG %eax, rINST # fp[A]<- low part
SET_VREG_HIGH %edx, rINST # fp[A+1]<- high part
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2709,7 +2647,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL(artGetObjStaticFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 1
@@ -2741,7 +2679,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL(artGetBooleanStaticFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
@@ -2773,7 +2711,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL(artGetByteStaticFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
@@ -2805,7 +2743,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL(artGetCharStaticFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
@@ -2837,7 +2775,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL(artGetShortStaticFromCode)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if 0
@@ -2869,9 +2807,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet32StaticFromCode)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2894,9 +2832,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet64IndirectStaticFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2912,9 +2850,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpSputObject)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -2939,9 +2877,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet8StaticFromCode)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2967,9 +2905,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet8StaticFromCode)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -2995,9 +2933,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet16StaticFromCode)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -3023,9 +2961,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet16StaticFromCode)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -3049,9 +2987,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 110
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeVirtual)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
/*
@@ -3082,9 +3020,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 111
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeSuper)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
/*
@@ -3115,9 +3053,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 112
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeDirect)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -3141,9 +3079,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 113
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeStatic)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -3168,9 +3106,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 114
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeInterface)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
/*
@@ -3215,9 +3153,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 116
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeVirtualRange)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -3241,9 +3179,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 117
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeSuperRange)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -3267,9 +3205,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 118
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeDirectRange)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -3293,9 +3231,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 119
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeStaticRange)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -3319,9 +3257,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 120
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeInterfaceRange)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -4047,10 +3985,10 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
je common_errDivideByZero
movl %eax, %edx
orl %ecx, %edx
- test $0xFFFFFF00, %edx # If both arguments are less
+ testl $0xFFFFFF00, %edx # If both arguments are less
# than 8-bit and +ve
jz .Lop_div_int_8 # Do 8-bit divide
- test $0xFFFF0000, %edx # If both arguments are less
+ testl $0xFFFF0000, %edx # If both arguments are less
# than 16-bit and +ve
jz .Lop_div_int_16 # Do 16-bit divide
cmpl $-1, %ecx
@@ -4101,10 +4039,10 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
je common_errDivideByZero
movl %eax, %edx
orl %ecx, %edx
- test $0xFFFFFF00, %edx # If both arguments are less
+ testl $0xFFFFFF00, %edx # If both arguments are less
# than 8-bit and +ve
jz .Lop_rem_int_8 # Do 8-bit divide
- test $0xFFFF0000, %edx # If both arguments are less
+ testl $0xFFFF0000, %edx # If both arguments are less
# than 16-bit and +ve
jz .Lop_rem_int_16 # Do 16-bit divide
cmpl $-1, %ecx
@@ -4785,9 +4723,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
sarl $4, rINST # rINST <- B
GET_VREG %eax, rINST # eax <- vB
andb $0xf, %cl # ecx <- A
- mov rIBASE, LOCAL0(%esp)
+ movl rIBASE, rINST
imull (rFP,%ecx,4), %eax # trashes rIBASE/edx
- mov LOCAL0(%esp), rIBASE
+ movl rINST, rIBASE
SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
@@ -5514,11 +5452,11 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $4, %eax # eax <- B
GET_VREG %eax, %eax # eax <- vB
- movswl 2(rPC), %ecx # ecx <- ssssCCCC
+ movl rIBASE, %ecx
+ movswl 2(rPC), rIBASE # rIBASE <- ssssCCCC
andb $0xf, rINSTbl # rINST <- A
- mov rIBASE, LOCAL0(%esp)
- imull %ecx, %eax # trashes rIBASE/edx
- mov LOCAL0(%esp), rIBASE
+ imull rIBASE, %eax # trashes rIBASE/edx
+ movl %ecx, rIBASE
SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5721,11 +5659,11 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
/* File: x86/op_mul_int_lit8.S */
/* mul/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
- movsbl 3(rPC), %ecx # ecx <- ssssssCC
+ movl rIBASE, %ecx
GET_VREG %eax, %eax # eax <- rBB
- mov rIBASE, LOCAL0(%esp)
- imull %ecx, %eax # trashes rIBASE/edx
- mov LOCAL0(%esp), rIBASE
+ movsbl 3(rPC), rIBASE # rIBASE <- ssssssCC
+ imull rIBASE, %eax # trashes rIBASE/edx
+ movl %ecx, rIBASE
SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
@@ -5985,7 +5923,7 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
EXPORT_PC
call SYMBOL(artIGetObjectFromMterp) # (obj, offset)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $0xf,rINSTbl # rINST <- A
@@ -6037,9 +5975,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 232
movl rINST, OUT_ARG2(%esp)
call SYMBOL(MterpIputObjectQuick)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
/* ------------------------------ */
@@ -6062,9 +6000,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 233
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeVirtualQuick)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -6088,9 +6026,9 @@ SYMBOL(artMterpAsmInstructionStart) = .L_op_nop
REFRESH_INST 234
movl rINST, OUT_ARG3(%esp)
call SYMBOL(MterpInvokeVirtualQuickRange)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
@@ -12810,7 +12748,6 @@ SYMBOL(artMterpAsmAltInstructionEnd):
* has not yet been thrown. Just bail out to the reference interpreter to deal with it.
* TUNING: for consistency, we may want to just go ahead and handle these here.
*/
-#define MTERP_LOGGING 0
common_errDivideByZero:
EXPORT_PC
#if MTERP_LOGGING
@@ -12912,7 +12849,7 @@ MterpException:
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
call SYMBOL(MterpHandleException)
- testl %eax, %eax
+ testb %al, %al
jz MterpExceptionReturn
REFRESH_IBASE
movl OFF_FP_CODE_ITEM(rFP), %eax
@@ -12941,6 +12878,21 @@ MterpCheckSuspendAndContinue:
GOTO_NEXT
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ lea OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpLogOSR)
+#endif
+ movl $1, %eax
+ jmp MterpDone
+
+/*
* Bail out to reference interpreter.
*/
MterpFallback:
diff --git a/runtime/interpreter/mterp/x86/bincmp.S b/runtime/interpreter/mterp/x86/bincmp.S
index 27cf6ea6d4..c72a5cf9d4 100644
--- a/runtime/interpreter/mterp/x86/bincmp.S
+++ b/runtime/interpreter/mterp/x86/bincmp.S
@@ -11,18 +11,13 @@
GET_VREG %eax, %ecx # eax <- vA
sarl $$4, rINST # rINST <- B
cmpl VREG_ADDRESS(rINST), %eax # compare (vA, vB)
- movl $$2, %eax # assume not taken
+ movl $$2, rINST
j${revcmp} 1f
- movswl 2(rPC),%eax # Get signed branch offset
+ movswl 2(rPC), rINST # Get signed branch offset
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86/bindiv.S b/runtime/interpreter/mterp/x86/bindiv.S
index bb5b319c49..e87ba45546 100644
--- a/runtime/interpreter/mterp/x86/bindiv.S
+++ b/runtime/interpreter/mterp/x86/bindiv.S
@@ -13,10 +13,10 @@
je common_errDivideByZero
movl %eax, %edx
orl %ecx, %edx
- test $$0xFFFFFF00, %edx # If both arguments are less
+ testl $$0xFFFFFF00, %edx # If both arguments are less
# than 8-bit and +ve
jz .L${opcode}_8 # Do 8-bit divide
- test $$0xFFFF0000, %edx # If both arguments are less
+ testl $$0xFFFF0000, %edx # If both arguments are less
# than 16-bit and +ve
jz .L${opcode}_16 # Do 16-bit divide
cmpl $$-1, %ecx
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index 385e78499f..c67491e577 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -12,7 +12,6 @@
* has not yet been thrown. Just bail out to the reference interpreter to deal with it.
* TUNING: for consistency, we may want to just go ahead and handle these here.
*/
-#define MTERP_LOGGING 0
common_errDivideByZero:
EXPORT_PC
#if MTERP_LOGGING
@@ -114,7 +113,7 @@ MterpException:
lea OFF_FP_SHADOWFRAME(rFP), %ecx
movl %ecx, OUT_ARG1(%esp)
call SYMBOL(MterpHandleException)
- testl %eax, %eax
+ testb %al, %al
jz MterpExceptionReturn
REFRESH_IBASE
movl OFF_FP_CODE_ITEM(rFP), %eax
@@ -143,6 +142,21 @@ MterpCheckSuspendAndContinue:
GOTO_NEXT
/*
+ * On-stack replacement has happened, and now we've returned from the compiled method.
+ */
+MterpOnStackReplacement:
+#if MTERP_LOGGING
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ lea OFF_FP_SHADOWFRAME(rFP), %ecx
+ movl %ecx, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpLogOSR)
+#endif
+ movl $$1, %eax
+ jmp MterpDone
+
+/*
* Bail out to reference interpreter.
*/
MterpFallback:
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 0977b901e2..6bddaf9344 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -156,13 +156,26 @@ unspecified registers or condition codes.
#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
+#define MTERP_PROFILE_BRANCHES 1
+#define MTERP_LOGGING 0
+
/*
- *
- * The reference interpreter performs explicit suspect checks, which is somewhat wasteful.
- * Dalvik's interpreter folded suspend checks into the jump table mechanism, and eventually
- * mterp should do so as well.
+ * Profile branch. rINST should contain the offset. %eax is scratch.
*/
-#define MTERP_SUSPEND 0
+.macro MTERP_PROFILE_BRANCH
+#ifdef MTERP_PROFILE_BRANCHES
+ EXPORT_PC
+ movl rSELF, %eax
+ movl %eax, OUT_ARG0(%esp)
+ leal OFF_FP_SHADOWFRAME(rFP), %eax
+ movl %eax, OUT_ARG1(%esp)
+ movl rINST, OUT_ARG2(%esp)
+ call SYMBOL(MterpProfileBranch)
+ testb %al, %al
+ jnz MterpOnStackReplacement
+ RESTORE_IBASE
+#endif
+.endm
/*
* "export" the PC to dex_pc field in the shadow frame, f/b/o future exception objects. Must
@@ -182,13 +195,21 @@ unspecified registers or condition codes.
/*
* Refresh handler table.
+ */
+.macro REFRESH_IBASE
+ movl rSELF, rIBASE
+ movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
+.endm
+
+/*
+ * Refresh handler table.
* IBase handles uses the caller save register so we must restore it after each call.
* Also it is used as a result of some 64-bit operations (like imul) and we should
* restore it in such cases also.
*
* TODO: Consider spilling the IBase instead of restoring it from Thread structure.
*/
-.macro REFRESH_IBASE
+.macro RESTORE_IBASE
movl rSELF, rIBASE
movl THREAD_CURRENT_IBASE_OFFSET(rIBASE), rIBASE
.endm
@@ -196,7 +217,7 @@ unspecified registers or condition codes.
/*
* If rSELF is already loaded then we can use it from known reg.
*/
-.macro REFRESH_IBASE_FROM_SELF _reg
+.macro RESTORE_IBASE_FROM_SELF _reg
movl THREAD_CURRENT_IBASE_OFFSET(\_reg), rIBASE
.endm
diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S
index 054fbfdf69..bbd88cf40b 100644
--- a/runtime/interpreter/mterp/x86/invoke.S
+++ b/runtime/interpreter/mterp/x86/invoke.S
@@ -14,7 +14,7 @@
REFRESH_INST ${opnum}
movl rINST, OUT_ARG3(%esp)
call SYMBOL($helper)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_aget_object.S b/runtime/interpreter/mterp/x86/op_aget_object.S
index cbfb50cb09..35ec053854 100644
--- a/runtime/interpreter/mterp/x86/op_aget_object.S
+++ b/runtime/interpreter/mterp/x86/op_aget_object.S
@@ -13,7 +13,7 @@
movl %ecx, OUT_ARG1(%esp)
call SYMBOL(artAGetObjectFromMterp) # (array, index)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
SET_VREG_OBJECT %eax, rINST
diff --git a/runtime/interpreter/mterp/x86/op_aput_object.S b/runtime/interpreter/mterp/x86/op_aput_object.S
index 9cfc2213d2..980b26a401 100644
--- a/runtime/interpreter/mterp/x86/op_aput_object.S
+++ b/runtime/interpreter/mterp/x86/op_aput_object.S
@@ -9,7 +9,7 @@
REFRESH_INST ${opnum}
movl rINST, OUT_ARG2(%esp)
call SYMBOL(MterpAputObject) # (array, index)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_check_cast.S b/runtime/interpreter/mterp/x86/op_check_cast.S
index ae2ff9ea21..d090aa3785 100644
--- a/runtime/interpreter/mterp/x86/op_check_cast.S
+++ b/runtime/interpreter/mterp/x86/op_check_cast.S
@@ -12,7 +12,7 @@
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpCheckCast) # (index, &obj, method, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_const_class.S b/runtime/interpreter/mterp/x86/op_const_class.S
index 343e110f71..60be789214 100644
--- a/runtime/interpreter/mterp/x86/op_const_class.S
+++ b/runtime/interpreter/mterp/x86/op_const_class.S
@@ -8,7 +8,7 @@
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpConstClass) # (index, tgt_reg, shadow_frame, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_const_string.S b/runtime/interpreter/mterp/x86/op_const_string.S
index bbac69ced4..ff93b232d6 100644
--- a/runtime/interpreter/mterp/x86/op_const_string.S
+++ b/runtime/interpreter/mterp/x86/op_const_string.S
@@ -8,7 +8,7 @@
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S
index 4236807dd3..e7f952a306 100644
--- a/runtime/interpreter/mterp/x86/op_const_string_jumbo.S
+++ b/runtime/interpreter/mterp/x86/op_const_string_jumbo.S
@@ -8,7 +8,7 @@
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpConstString) # (index, tgt_reg, shadow_frame, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_fill_array_data.S b/runtime/interpreter/mterp/x86/op_fill_array_data.S
index 004aed9872..5855284901 100644
--- a/runtime/interpreter/mterp/x86/op_fill_array_data.S
+++ b/runtime/interpreter/mterp/x86/op_fill_array_data.S
@@ -7,6 +7,6 @@
movl %ecx, OUT_ARG1(%esp)
call SYMBOL(MterpFillArrayData) # (obj, payload)
REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_filled_new_array.S b/runtime/interpreter/mterp/x86/op_filled_new_array.S
index a2bac29bc1..35b2fe8dfc 100644
--- a/runtime/interpreter/mterp/x86/op_filled_new_array.S
+++ b/runtime/interpreter/mterp/x86/op_filled_new_array.S
@@ -15,6 +15,6 @@
movl %ecx, OUT_ARG2(%esp)
call SYMBOL($helper)
REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 3
diff --git a/runtime/interpreter/mterp/x86/op_goto.S b/runtime/interpreter/mterp/x86/op_goto.S
index 411399d3ad..9a87361c8e 100644
--- a/runtime/interpreter/mterp/x86/op_goto.S
+++ b/runtime/interpreter/mterp/x86/op_goto.S
@@ -5,15 +5,10 @@
* double to get a byte offset.
*/
/* goto +AA */
- movsbl rINSTbl, %eax # eax <- ssssssAA
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ movsbl rINSTbl, rINST # rINST <- ssssssAA
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # rINST <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 1f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86/op_goto_16.S b/runtime/interpreter/mterp/x86/op_goto_16.S
index 4f04f9e479..a25c31b2d0 100644
--- a/runtime/interpreter/mterp/x86/op_goto_16.S
+++ b/runtime/interpreter/mterp/x86/op_goto_16.S
@@ -5,15 +5,10 @@
* double to get a byte offset.
*/
/* goto/16 +AAAA */
- movswl 2(rPC), %eax # eax <- ssssAAAA
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ movswl 2(rPC), rINST # rINST <- ssssAAAA
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # rINST <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 1f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86/op_goto_32.S b/runtime/interpreter/mterp/x86/op_goto_32.S
index 48f6e5afd7..159128be1c 100644
--- a/runtime/interpreter/mterp/x86/op_goto_32.S
+++ b/runtime/interpreter/mterp/x86/op_goto_32.S
@@ -10,15 +10,10 @@
* offset to byte offset.
*/
/* goto/32 +AAAAAAAA */
- movl 2(rPC), %eax # eax <- AAAAAAAA
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ movl 2(rPC), rINST # rINST <- AAAAAAAA
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # rINST <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 1f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86/op_iget.S b/runtime/interpreter/mterp/x86/op_iget.S
index 99326105c0..e3304ba6a7 100644
--- a/runtime/interpreter/mterp/x86/op_iget.S
+++ b/runtime/interpreter/mterp/x86/op_iget.S
@@ -17,7 +17,7 @@
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL($helper)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $$0xf, rINSTbl # rINST <- A
diff --git a/runtime/interpreter/mterp/x86/op_iget_object_quick.S b/runtime/interpreter/mterp/x86/op_iget_object_quick.S
index fe166948c9..b1551a0179 100644
--- a/runtime/interpreter/mterp/x86/op_iget_object_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iget_object_quick.S
@@ -9,7 +9,7 @@
EXPORT_PC
call SYMBOL(artIGetObjectFromMterp) # (obj, offset)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException # bail out
andb $$0xf,rINSTbl # rINST <- A
diff --git a/runtime/interpreter/mterp/x86/op_iget_wide.S b/runtime/interpreter/mterp/x86/op_iget_wide.S
index 92126b4473..a5d7e6937d 100644
--- a/runtime/interpreter/mterp/x86/op_iget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_iget_wide.S
@@ -21,5 +21,5 @@
andb $$0xf, rINSTbl # rINST <- A
SET_VREG %eax, rINST
SET_VREG_HIGH %edx, rINST
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_instance_of.S b/runtime/interpreter/mterp/x86/op_instance_of.S
index fd5bf44c78..e6fe5b2cec 100644
--- a/runtime/interpreter/mterp/x86/op_instance_of.S
+++ b/runtime/interpreter/mterp/x86/op_instance_of.S
@@ -18,7 +18,7 @@
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpInstanceOf) # (index, &obj, method, self)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
andb $$0xf, rINSTbl # rINSTbl <- A
diff --git a/runtime/interpreter/mterp/x86/op_iput.S b/runtime/interpreter/mterp/x86/op_iput.S
index 13cfe5ca69..c847e2dc88 100644
--- a/runtime/interpreter/mterp/x86/op_iput.S
+++ b/runtime/interpreter/mterp/x86/op_iput.S
@@ -19,7 +19,7 @@
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL($handler)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iput_object.S b/runtime/interpreter/mterp/x86/op_iput_object.S
index f63075c503..e0136970b0 100644
--- a/runtime/interpreter/mterp/x86/op_iput_object.S
+++ b/runtime/interpreter/mterp/x86/op_iput_object.S
@@ -7,7 +7,7 @@
movl rSELF, %eax
movl %eax, OUT_ARG3(%esp)
call SYMBOL(MterpIputObject)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iput_object_quick.S b/runtime/interpreter/mterp/x86/op_iput_object_quick.S
index d54b1b772f..cb779295b7 100644
--- a/runtime/interpreter/mterp/x86/op_iput_object_quick.S
+++ b/runtime/interpreter/mterp/x86/op_iput_object_quick.S
@@ -5,7 +5,7 @@
REFRESH_INST ${opnum}
movl rINST, OUT_ARG2(%esp)
call SYMBOL(MterpIputObjectQuick)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_iput_wide.S b/runtime/interpreter/mterp/x86/op_iput_wide.S
index 573e14d663..122eecf43f 100644
--- a/runtime/interpreter/mterp/x86/op_iput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_iput_wide.S
@@ -13,7 +13,7 @@
movl OFF_FP_METHOD(rFP), %eax
movl %eax, OUT_ARG3(%esp) # referrer
call SYMBOL(artSet64InstanceFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpPossibleException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_monitor_enter.S b/runtime/interpreter/mterp/x86/op_monitor_enter.S
index 9e885bde93..b35c68488a 100644
--- a/runtime/interpreter/mterp/x86/op_monitor_enter.S
+++ b/runtime/interpreter/mterp/x86/op_monitor_enter.S
@@ -8,7 +8,7 @@
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
call SYMBOL(artLockObjectFromCode) # (object, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_monitor_exit.S b/runtime/interpreter/mterp/x86/op_monitor_exit.S
index 090480056a..2d17d5e7c5 100644
--- a/runtime/interpreter/mterp/x86/op_monitor_exit.S
+++ b/runtime/interpreter/mterp/x86/op_monitor_exit.S
@@ -12,7 +12,7 @@
movl rSELF, %eax
movl %eax, OUT_ARG1(%esp)
call SYMBOL(artUnlockObjectFromCode) # (object, self)
- REFRESH_IBASE
- testl %eax, %eax
+ RESTORE_IBASE
+ testb %al, %al
jnz MterpException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S
index f92a28e46a..da699ae19b 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int_2addr.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int_2addr.S
@@ -3,8 +3,8 @@
sarl $$4, rINST # rINST <- B
GET_VREG %eax, rINST # eax <- vB
andb $$0xf, %cl # ecx <- A
- mov rIBASE, LOCAL0(%esp)
+ movl rIBASE, rINST
imull (rFP,%ecx,4), %eax # trashes rIBASE/edx
- mov LOCAL0(%esp), rIBASE
+ movl rINST, rIBASE
SET_VREG %eax, %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S
index 31ab613805..056f491bef 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int_lit16.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int_lit16.S
@@ -3,10 +3,10 @@
movzbl rINSTbl, %eax # eax <- 000000BA
sarl $$4, %eax # eax <- B
GET_VREG %eax, %eax # eax <- vB
- movswl 2(rPC), %ecx # ecx <- ssssCCCC
+ movl rIBASE, %ecx
+ movswl 2(rPC), rIBASE # rIBASE <- ssssCCCC
andb $$0xf, rINSTbl # rINST <- A
- mov rIBASE, LOCAL0(%esp)
- imull %ecx, %eax # trashes rIBASE/edx
- mov LOCAL0(%esp), rIBASE
+ imull rIBASE, %eax # trashes rIBASE/edx
+ movl %ecx, rIBASE
SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S
index 6637aa7384..59b384426c 100644
--- a/runtime/interpreter/mterp/x86/op_mul_int_lit8.S
+++ b/runtime/interpreter/mterp/x86/op_mul_int_lit8.S
@@ -1,9 +1,9 @@
/* mul/lit8 vAA, vBB, #+CC */
movzbl 2(rPC), %eax # eax <- BB
- movsbl 3(rPC), %ecx # ecx <- ssssssCC
+ movl rIBASE, %ecx
GET_VREG %eax, %eax # eax <- rBB
- mov rIBASE, LOCAL0(%esp)
- imull %ecx, %eax # trashes rIBASE/edx
- mov LOCAL0(%esp), rIBASE
+ movsbl 3(rPC), rIBASE # rIBASE <- ssssssCC
+ imull rIBASE, %eax # trashes rIBASE/edx
+ movl %ecx, rIBASE
SET_VREG %eax, rINST
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_new_array.S b/runtime/interpreter/mterp/x86/op_new_array.S
index 24904774e1..16226e989c 100644
--- a/runtime/interpreter/mterp/x86/op_new_array.S
+++ b/runtime/interpreter/mterp/x86/op_new_array.S
@@ -15,7 +15,7 @@
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpNewArray)
- REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ RESTORE_IBASE
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_new_instance.S b/runtime/interpreter/mterp/x86/op_new_instance.S
index 712a5ebe96..f976accb1e 100644
--- a/runtime/interpreter/mterp/x86/op_new_instance.S
+++ b/runtime/interpreter/mterp/x86/op_new_instance.S
@@ -10,7 +10,7 @@
REFRESH_INST ${opnum}
movl rINST, OUT_ARG2(%esp)
call SYMBOL(MterpNewInstance)
- REFRESH_IBASE
- testl %eax, %eax # 0 means an exception is thrown
+ RESTORE_IBASE
+ testb %al, %al # 0 means an exception is thrown
jz MterpPossibleException
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_packed_switch.S b/runtime/interpreter/mterp/x86/op_packed_switch.S
index 230b58e02b..e33cf75499 100644
--- a/runtime/interpreter/mterp/x86/op_packed_switch.S
+++ b/runtime/interpreter/mterp/x86/op_packed_switch.S
@@ -15,15 +15,11 @@
movl %eax, OUT_ARG1(%esp) # ARG1 <- vAA
movl %ecx, OUT_ARG0(%esp) # ARG0 <- switchData
call SYMBOL($func)
- addl %eax, %eax
- leal (rPC, %eax), rPC
+ movl %eax, rINST
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST
+ leal (rPC, rINST), rPC
FETCH_INST
REFRESH_IBASE
- jg 1f
-#if MTERP_SUSPEND
- # REFRESH_IBASE - we did it above.
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-1:
+ jle MterpCheckSuspendAndContinue
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86/op_sget.S b/runtime/interpreter/mterp/x86/op_sget.S
index ec964581af..0e9a3d82da 100644
--- a/runtime/interpreter/mterp/x86/op_sget.S
+++ b/runtime/interpreter/mterp/x86/op_sget.S
@@ -15,7 +15,7 @@
movl %ecx, OUT_ARG2(%esp) # self
call SYMBOL($helper)
movl rSELF, %ecx
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
cmpl $$0, THREAD_EXCEPTION_OFFSET(%ecx)
jnz MterpException
.if $is_object
diff --git a/runtime/interpreter/mterp/x86/op_sget_wide.S b/runtime/interpreter/mterp/x86/op_sget_wide.S
index 833f266dd5..2b603034c6 100644
--- a/runtime/interpreter/mterp/x86/op_sget_wide.S
+++ b/runtime/interpreter/mterp/x86/op_sget_wide.S
@@ -17,5 +17,5 @@
jnz MterpException
SET_VREG %eax, rINST # fp[A]<- low part
SET_VREG_HIGH %edx, rINST # fp[A+1]<- high part
- REFRESH_IBASE_FROM_SELF %ecx
+ RESTORE_IBASE_FROM_SELF %ecx
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_sput.S b/runtime/interpreter/mterp/x86/op_sput.S
index a199281088..0b5de0953d 100644
--- a/runtime/interpreter/mterp/x86/op_sput.S
+++ b/runtime/interpreter/mterp/x86/op_sput.S
@@ -16,7 +16,7 @@
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL($helper)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_sput_object.S b/runtime/interpreter/mterp/x86/op_sput_object.S
index e3e57fc87b..0db517723b 100644
--- a/runtime/interpreter/mterp/x86/op_sput_object.S
+++ b/runtime/interpreter/mterp/x86/op_sput_object.S
@@ -7,7 +7,7 @@
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp)
call SYMBOL(MterpSputObject)
- testl %eax, %eax
+ testb %al, %al
jz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/op_sput_wide.S b/runtime/interpreter/mterp/x86/op_sput_wide.S
index 7544838d52..19cff0db5a 100644
--- a/runtime/interpreter/mterp/x86/op_sput_wide.S
+++ b/runtime/interpreter/mterp/x86/op_sput_wide.S
@@ -14,7 +14,7 @@
movl rSELF, %ecx
movl %ecx, OUT_ARG3(%esp) # self
call SYMBOL(artSet64IndirectStaticFromMterp)
- testl %eax, %eax
+ testb %al, %al
jnz MterpException
- REFRESH_IBASE
+ RESTORE_IBASE
ADVANCE_PC_FETCH_AND_GOTO_NEXT 2
diff --git a/runtime/interpreter/mterp/x86/zcmp.S b/runtime/interpreter/mterp/x86/zcmp.S
index 5ce4f0f6a7..0f28d1acd8 100644
--- a/runtime/interpreter/mterp/x86/zcmp.S
+++ b/runtime/interpreter/mterp/x86/zcmp.S
@@ -7,18 +7,13 @@
*/
/* if-cmp vAA, +BBBB */
cmpl $$0, VREG_ADDRESS(rINST) # compare (vA, 0)
- movl $$2, %eax # assume branch not taken
+ movl $$2, rINST
j${revcmp} 1f
- movswl 2(rPC),%eax # fetch signed displacement
+ movswl 2(rPC), rINST # fetch signed displacement
1:
- addl %eax, %eax # eax <- AA * 2
- leal (rPC, %eax), rPC
+ MTERP_PROFILE_BRANCH
+ addl rINST, rINST # eax <- AA * 2
+ leal (rPC, rINST), rPC
FETCH_INST
- jg 2f # AA * 2 > 0 => no suspend check
-#if MTERP_SUSPEND
- REFRESH_IBASE
-#else
- jmp MterpCheckSuspendAndContinue
-#endif
-2:
+ jle MterpCheckSuspendAndContinue # AA * 2 <= 0 => suspend check
GOTO_NEXT
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 60ad0cbb10..0e175b85eb 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -261,6 +261,16 @@ void UnstartedRuntime::UnstartedClassGetDeclaredField(
}
}
+void UnstartedRuntime::UnstartedClassGetEnclosingClass(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> klass(hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsClass()));
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ result->SetL(nullptr);
+ }
+ result->SetL(klass->GetDexFile().GetEnclosingClass(klass));
+}
+
void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 047e906614..6d4d711645 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -24,6 +24,7 @@
V(ClassClassForName, "java.lang.Class java.lang.Class.classForName(java.lang.String, boolean, java.lang.ClassLoader)") \
V(ClassNewInstance, "java.lang.Object java.lang.Class.newInstance()") \
V(ClassGetDeclaredField, "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") \
+ V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index c6ec5b083a..1a28733efd 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -113,8 +113,7 @@ bool Jit::LoadCompiler(std::string* error_msg) {
*error_msg = oss.str();
return false;
}
- jit_load_ = reinterpret_cast<void* (*)(CompilerCallbacks**, bool*)>(
- dlsym(jit_library_handle_, "jit_load"));
+ jit_load_ = reinterpret_cast<void* (*)(bool*)>(dlsym(jit_library_handle_, "jit_load"));
if (jit_load_ == nullptr) {
dlclose(jit_library_handle_);
*error_msg = "JIT couldn't find jit_load entry point";
@@ -141,23 +140,15 @@ bool Jit::LoadCompiler(std::string* error_msg) {
*error_msg = "JIT couldn't find jit_types_loaded entry point";
return false;
}
- CompilerCallbacks* callbacks = nullptr;
bool will_generate_debug_symbols = false;
VLOG(jit) << "Calling JitLoad interpreter_only="
<< Runtime::Current()->GetInstrumentation()->InterpretOnly();
- jit_compiler_handle_ = (jit_load_)(&callbacks, &will_generate_debug_symbols);
+ jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols);
if (jit_compiler_handle_ == nullptr) {
dlclose(jit_library_handle_);
*error_msg = "JIT couldn't load compiler";
return false;
}
- if (callbacks == nullptr) {
- dlclose(jit_library_handle_);
- *error_msg = "JIT compiler callbacks were not set";
- jit_compiler_handle_ = nullptr;
- return false;
- }
- compiler_callbacks_ = callbacks;
generate_debug_info_ = will_generate_debug_symbols;
return true;
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 042da92b3b..109ca3dbd1 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -32,7 +32,6 @@
namespace art {
class ArtMethod;
-class CompilerCallbacks;
struct RuntimeArgumentMap;
namespace jit {
@@ -55,9 +54,6 @@ class Jit {
size_t warmup_threshold,
size_t osr_threshold);
void CreateThreadPool();
- CompilerCallbacks* GetCompilerCallbacks() {
- return compiler_callbacks_;
- }
const JitCodeCache* GetCodeCache() const {
return code_cache_.get();
}
@@ -108,7 +104,7 @@ class Jit {
// JIT compiler
void* jit_library_handle_;
void* jit_compiler_handle_;
- void* (*jit_load_)(CompilerCallbacks**, bool*);
+ void* (*jit_load_)(bool*);
void (*jit_unload_)(void*);
bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool);
void (*jit_types_loaded_)(void*, mirror::Class**, size_t count);
@@ -119,7 +115,6 @@ class Jit {
std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_;
std::unique_ptr<jit::JitCodeCache> code_cache_;
- CompilerCallbacks* compiler_callbacks_; // Owned by the jit compiler.
bool save_profiling_info_;
bool generate_debug_info_;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 74ff741d93..478b164597 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -123,9 +123,11 @@ JitCodeCache::JitCodeCache(MemMap* code_map,
current_capacity_(initial_code_capacity + initial_data_capacity),
code_end_(initial_code_capacity),
data_end_(initial_data_capacity),
- has_done_one_collection_(false),
+ has_done_full_collection_(false),
last_update_time_ns_(0),
garbage_collect_code_(garbage_collect_code),
+ used_memory_for_data_(0),
+ used_memory_for_code_(0),
number_of_compilations_(0) {
DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
@@ -232,25 +234,20 @@ static uintptr_t FromCodeToAllocation(const void* code) {
void JitCodeCache::FreeCode(const void* code_ptr, ArtMethod* method ATTRIBUTE_UNUSED) {
uintptr_t allocation = FromCodeToAllocation(code_ptr);
const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
- const uint8_t* data = method_header->GetNativeGcMap();
// Notify native debugger that we are about to remove the code.
// It does nothing if we are not using native debugger.
DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr));
- if (data != nullptr) {
- mspace_free(data_mspace_, const_cast<uint8_t*>(data));
- }
- data = method_header->GetMappingTable();
- if (data != nullptr) {
- mspace_free(data_mspace_, const_cast<uint8_t*>(data));
- }
+
+ FreeData(const_cast<uint8_t*>(method_header->GetNativeGcMap()));
+ FreeData(const_cast<uint8_t*>(method_header->GetMappingTable()));
// Use the offset directly to prevent sanity check that the method is
// compiled with optimizing.
// TODO(ngeoffray): Clean up.
if (method_header->vmap_table_offset_ != 0) {
- data = method_header->code_ - method_header->vmap_table_offset_;
- mspace_free(data_mspace_, const_cast<uint8_t*>(data));
+ const uint8_t* data = method_header->code_ - method_header->vmap_table_offset_;
+ FreeData(const_cast<uint8_t*>(data));
}
- mspace_free(code_mspace_, reinterpret_cast<uint8_t*>(allocation));
+ FreeCode(reinterpret_cast<uint8_t*>(allocation));
}
void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) {
@@ -281,7 +278,7 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) {
ProfilingInfo* info = *it;
if (alloc.ContainsUnsafe(info->GetMethod())) {
info->GetMethod()->SetProfilingInfo(nullptr);
- mspace_free(data_mspace_, reinterpret_cast<uint8_t*>(info));
+ FreeData(reinterpret_cast<uint8_t*>(info));
it = profiling_infos_.erase(it);
} else {
++it;
@@ -307,19 +304,18 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
OatQuickMethodHeader* method_header = nullptr;
uint8_t* code_ptr = nullptr;
+ uint8_t* memory = nullptr;
{
ScopedThreadSuspension sts(self, kSuspended);
MutexLock mu(self, lock_);
WaitForPotentialCollectionToComplete(self);
{
ScopedCodeCacheWrite scc(code_map_.get());
- uint8_t* result = reinterpret_cast<uint8_t*>(
- mspace_memalign(code_mspace_, alignment, total_size));
- if (result == nullptr) {
+ memory = AllocateCode(total_size);
+ if (memory == nullptr) {
return nullptr;
}
- code_ptr = result + header_size;
- DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(code_ptr), alignment);
+ code_ptr = memory + header_size;
std::copy(code, code + code_size, code_ptr);
method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
@@ -376,9 +372,7 @@ size_t JitCodeCache::CodeCacheSize() {
}
size_t JitCodeCache::CodeCacheSizeLocked() {
- size_t bytes_allocated = 0;
- mspace_inspect_all(code_mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated);
- return bytes_allocated;
+ return used_memory_for_code_;
}
size_t JitCodeCache::DataCacheSize() {
@@ -387,9 +381,7 @@ size_t JitCodeCache::DataCacheSize() {
}
size_t JitCodeCache::DataCacheSizeLocked() {
- size_t bytes_allocated = 0;
- mspace_inspect_all(data_mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated);
- return bytes_allocated;
+ return used_memory_for_data_;
}
size_t JitCodeCache::NumberOfCompiledCode() {
@@ -399,7 +391,7 @@ size_t JitCodeCache::NumberOfCompiledCode() {
void JitCodeCache::ClearData(Thread* self, void* data) {
MutexLock mu(self, lock_);
- mspace_free(data_mspace_, data);
+ FreeData(reinterpret_cast<uint8_t*>(data));
}
uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
@@ -410,7 +402,7 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
ScopedThreadSuspension sts(self, kSuspended);
MutexLock mu(self, lock_);
WaitForPotentialCollectionToComplete(self);
- result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size));
+ result = AllocateData(size);
}
if (result == nullptr) {
@@ -419,7 +411,7 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
ScopedThreadSuspension sts(self, kSuspended);
MutexLock mu(self, lock_);
WaitForPotentialCollectionToComplete(self);
- result = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, size));
+ result = AllocateData(size);
}
return result;
@@ -534,8 +526,56 @@ bool JitCodeCache::IncreaseCodeCacheCapacity() {
return true;
}
+void JitCodeCache::MarkCompiledCodeOnThreadStacks(Thread* self) {
+ Barrier barrier(0);
+ size_t threads_running_checkpoint = 0;
+ MarkCodeClosure closure(this, &barrier);
+ threads_running_checkpoint = Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
+ // Now that we have run our checkpoint, move to a suspended state and wait
+ // for other threads to run the checkpoint.
+ ScopedThreadSuspension sts(self, kSuspended);
+ if (threads_running_checkpoint != 0) {
+ barrier.Increment(self, threads_running_checkpoint);
+ }
+}
+
+void JitCodeCache::RemoveUnusedCode(Thread* self) {
+ // Clear the osr map, chances are most of the code in it is now dead.
+ {
+ MutexLock mu(self, lock_);
+ osr_code_map_.clear();
+ }
+
+ // Run a checkpoint on all threads to mark the JIT compiled code they are running.
+ MarkCompiledCodeOnThreadStacks(self);
+
+ // Iterate over all compiled code and remove entries that are not marked and not
+ // the entrypoint of their corresponding ArtMethod.
+ {
+ MutexLock mu(self, lock_);
+ ScopedCodeCacheWrite scc(code_map_.get());
+ for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
+ const void* code_ptr = it->first;
+ ArtMethod* method = it->second;
+ uintptr_t allocation = FromCodeToAllocation(code_ptr);
+ const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+ if ((method->GetEntryPointFromQuickCompiledCode() != method_header->GetEntryPoint()) &&
+ !GetLiveBitmap()->Test(allocation)) {
+ FreeCode(code_ptr, method);
+ it = method_code_map_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
void JitCodeCache::GarbageCollectCache(Thread* self) {
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (!garbage_collect_code_) {
+ MutexLock mu(self, lock_);
+ IncreaseCodeCacheCapacity();
+ return;
+ }
// Wait for an existing collection, or let everyone know we are starting one.
{
@@ -544,42 +584,75 @@ void JitCodeCache::GarbageCollectCache(Thread* self) {
if (WaitForPotentialCollectionToComplete(self)) {
return;
} else {
+ live_bitmap_.reset(CodeCacheBitmap::Create(
+ "code-cache-bitmap",
+ reinterpret_cast<uintptr_t>(code_map_->Begin()),
+ reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2)));
collection_in_progress_ = true;
}
}
- // Check if we just need to grow the capacity. If we don't, allocate the bitmap while
- // we hold the lock.
+ // Check if we want to do a full collection.
+ bool do_full_collection = true;
{
MutexLock mu(self, lock_);
- if (!garbage_collect_code_) {
+ if (current_capacity_ == max_capacity_) {
+ // Always do a full collection when the code cache is full.
+ do_full_collection = true;
+ } else if (current_capacity_ < kReservedCapacity) {
+ // Do a partial collection until we hit the reserved capacity limit.
+ do_full_collection = false;
+ } else if (has_done_full_collection_) {
+ // Do a partial collection if we have done a full collection in the last
+ // collection round.
+ do_full_collection = false;
+ }
+ }
+
+ if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
+ LOG(INFO) << "Do "
+ << (do_full_collection ? "full" : "partial")
+ << " code cache collection, code="
+ << PrettySize(CodeCacheSize())
+ << ", data=" << PrettySize(DataCacheSize());
+ }
+
+ if (do_full_collection) {
+ DoFullCollection(self);
+ } else {
+ RemoveUnusedCode(self);
+ }
+
+ {
+ MutexLock mu(self, lock_);
+ if (!do_full_collection) {
+ has_done_full_collection_ = false;
IncreaseCodeCacheCapacity();
- NotifyCollectionDone(self);
- return;
- } else if (has_done_one_collection_ && IncreaseCodeCacheCapacity()) {
- has_done_one_collection_ = false;
- NotifyCollectionDone(self);
- return;
} else {
- live_bitmap_.reset(CodeCacheBitmap::Create(
- "code-cache-bitmap",
- reinterpret_cast<uintptr_t>(code_map_->Begin()),
- reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2)));
+ has_done_full_collection_ = true;
}
+ live_bitmap_.reset(nullptr);
+ NotifyCollectionDone(self);
}
if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
- LOG(INFO) << "Clearing code cache, code="
+ LOG(INFO) << "After code cache collection, code="
<< PrettySize(CodeCacheSize())
<< ", data=" << PrettySize(DataCacheSize());
}
- // Walk over all compiled methods and set the entry points of these
- // methods to interpreter.
+}
+
+void JitCodeCache::DoFullCollection(Thread* self) {
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
{
MutexLock mu(self, lock_);
+ // Walk over all compiled methods and set the entry points of these
+ // methods to interpreter.
for (auto& it : method_code_map_) {
instrumentation->UpdateMethodsCode(it.second, GetQuickToInterpreterBridge());
}
+
+ // Clear the profiling info of methods that are not being compiled.
for (ProfilingInfo* info : profiling_infos_) {
if (!info->IsMethodBeingCompiled()) {
info->GetMethod()->SetProfilingInfo(nullptr);
@@ -592,19 +665,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) {
}
// Run a checkpoint on all threads to mark the JIT compiled code they are running.
- {
- Barrier barrier(0);
- size_t threads_running_checkpoint = 0;
- MarkCodeClosure closure(this, &barrier);
- threads_running_checkpoint =
- Runtime::Current()->GetThreadList()->RunCheckpoint(&closure);
- // Now that we have run our checkpoint, move to a suspended state and wait
- // for other threads to run the checkpoint.
- ScopedThreadSuspension sts(self, kSuspended);
- if (threads_running_checkpoint != 0) {
- barrier.Increment(self, threads_running_checkpoint);
- }
- }
+ MarkCompiledCodeOnThreadStacks(self);
{
MutexLock mu(self, lock_);
@@ -628,27 +689,16 @@ void JitCodeCache::GarbageCollectCache(Thread* self) {
}
}
- void* data_mspace = data_mspace_;
// Free all profiling infos of methods that were not being compiled.
auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(),
- [data_mspace] (ProfilingInfo* info) {
+ [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS {
if (info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) {
- mspace_free(data_mspace, reinterpret_cast<uint8_t*>(info));
+ FreeData(reinterpret_cast<uint8_t*>(info));
return true;
}
return false;
});
profiling_infos_.erase(profiling_kept_end, profiling_infos_.end());
-
- live_bitmap_.reset(nullptr);
- has_done_one_collection_ = true;
- NotifyCollectionDone(self);
- }
-
- if (!kIsDebugBuild || VLOG_IS_ON(jit)) {
- LOG(INFO) << "After clearing code cache, code="
- << PrettySize(CodeCacheSize())
- << ", data=" << PrettySize(DataCacheSize());
}
}
@@ -718,7 +768,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self,
return info;
}
- uint8_t* data = reinterpret_cast<uint8_t*>(mspace_malloc(data_mspace_, profile_info_size));
+ uint8_t* data = AllocateData(profile_info_size);
if (data == nullptr) {
return nullptr;
}
@@ -809,5 +859,32 @@ void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method,
}
}
+uint8_t* JitCodeCache::AllocateCode(size_t code_size) {
+ size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ uint8_t* result = reinterpret_cast<uint8_t*>(
+ mspace_memalign(code_mspace_, alignment, code_size));
+ size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
+ // Ensure the header ends up at expected instruction alignment.
+ DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(result + header_size), alignment);
+ used_memory_for_code_ += mspace_usable_size(result);
+ return result;
+}
+
+void JitCodeCache::FreeCode(uint8_t* code) {
+ used_memory_for_code_ -= mspace_usable_size(code);
+ mspace_free(code_mspace_, code);
+}
+
+uint8_t* JitCodeCache::AllocateData(size_t data_size) {
+ void* result = mspace_malloc(data_mspace_, data_size);
+ used_memory_for_data_ += mspace_usable_size(result);
+ return reinterpret_cast<uint8_t*>(result);
+}
+
+void JitCodeCache::FreeData(uint8_t* data) {
+ used_memory_for_data_ -= mspace_usable_size(data);
+ mspace_free(data_mspace_, data);
+}
+
} // namespace jit
} // namespace art
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 71f5cda0c8..e5b8e6ca17 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -49,7 +49,10 @@ class JitCodeCache {
static constexpr size_t kMaxCapacity = 64 * MB;
// Put the default to a very low amount for debug builds to stress the code cache
// collection.
- static constexpr size_t kInitialCapacity = kIsDebugBuild ? 16 * KB : 64 * KB;
+ static constexpr size_t kInitialCapacity = kIsDebugBuild ? 8 * KB : 64 * KB;
+
+ // By default, do not GC until reaching 256KB.
+ static constexpr size_t kReservedCapacity = kInitialCapacity * 4;
// Create the code cache with a code + data capacity equal to "capacity", error message is passed
// in the out arg error_msg.
@@ -231,6 +234,18 @@ class JitCodeCache {
// Set the footprint limit of the code cache.
void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_);
+ void DoFullCollection(Thread* self)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void RemoveUnusedCode(Thread* self)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ void MarkCompiledCodeOnThreadStacks(Thread* self)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Lock for guarding allocations, collections, and the method_code_map_.
Mutex lock_;
// Condition to wait on during collection.
@@ -266,8 +281,8 @@ class JitCodeCache {
// The current footprint in bytes of the data portion of the code cache.
size_t data_end_ GUARDED_BY(lock_);
- // Whether a collection has already been done on the current capacity.
- bool has_done_one_collection_ GUARDED_BY(lock_);
+ // Whether a full collection has already been done on the current capacity.
+ bool has_done_full_collection_ GUARDED_BY(lock_);
// Last time the the code_cache was updated.
// It is atomic to avoid locking when reading it.
@@ -276,6 +291,17 @@ class JitCodeCache {
// Whether we can do garbage collection.
const bool garbage_collect_code_;
+ // The size in bytes of used memory for the data portion of the code cache.
+ size_t used_memory_for_data_ GUARDED_BY(lock_);
+
+ // The size in bytes of used memory for the code portion of the code cache.
+ size_t used_memory_for_code_ GUARDED_BY(lock_);
+
+ void FreeCode(uint8_t* code) REQUIRES(lock_);
+ uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_);
+ void FreeData(uint8_t* data) REQUIRES(lock_);
+ uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
+
// Number of compilations done throughout the lifetime of the JIT.
size_t number_of_compilations_ GUARDED_BY(lock_);
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 0aff1f7ec3..747b112f57 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -125,8 +125,8 @@ static constexpr const char kLineSeparator = '\n';
* app.apk,131232145,11,23,454,54
* app.apk:classes5.dex,218490184,39,13,49,1
**/
-bool ProfileCompilationInfo::Save(uint32_t fd) {
- DCHECK_GE(fd, 0u);
+bool ProfileCompilationInfo::Save(int fd) {
+ DCHECK_GE(fd, 0);
// TODO(calin): Profile this and see how much memory it takes. If too much,
// write to file directly.
std::ostringstream os;
@@ -232,8 +232,8 @@ static int GetLineFromBuffer(char* buffer, int n, int start_from, std::string& l
return new_line_pos == -1 ? new_line_pos : new_line_pos + 1;
}
-bool ProfileCompilationInfo::Load(uint32_t fd) {
- DCHECK_GE(fd, 0u);
+bool ProfileCompilationInfo::Load(int fd) {
+ DCHECK_GE(fd, 0);
std::string current_line;
const int kBufferSize = 1024;
@@ -343,7 +343,7 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>*
return os.str();
}
-bool ProfileCompilationInfo::Equals(ProfileCompilationInfo& other) {
+bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) {
return info_.Equals(other.info_);
}
diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h
index c388c4a42f..edc591c2eb 100644
--- a/runtime/jit/offline_profiling_info.h
+++ b/runtime/jit/offline_profiling_info.h
@@ -46,11 +46,11 @@ class ProfileCompilationInfo {
const std::vector<ArtMethod*>& methods);
// Loads profile information from the given file descriptor.
- bool Load(uint32_t fd);
+ bool Load(int fd);
// Loads the data from another ProfileCompilationInfo object.
bool Load(const ProfileCompilationInfo& info);
// Saves the profile data to the given file descriptor.
- bool Save(uint32_t fd);
+ bool Save(int fd);
// Returns the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
@@ -65,8 +65,7 @@ class ProfileCompilationInfo {
bool print_full_dex_location = true) const;
// For testing purposes.
- bool Equals(ProfileCompilationInfo& other);
- // Exposed for testing purpose.
+ bool Equals(const ProfileCompilationInfo& other);
static std::string GetProfileDexFileKey(const std::string& dex_location);
private:
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index c908b3920a..11156c6229 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -230,17 +230,16 @@ static bool CheckMapRequest(uint8_t* expected_ptr, void* actual_ptr, size_t byte
PLOG(WARNING) << StringPrintf("munmap(%p, %zd) failed", actual_ptr, byte_count);
}
- // We call this here so that we can try and generate a full error
- // message with the overlapping mapping. There's no guarantee that
- // that there will be an overlap though, since
- // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is
- // true, even if there is no overlap
- // - There might have been an overlap at the point of mmap, but the
- // overlapping region has since been unmapped.
- std::string error_detail;
- CheckNonOverlapping(expected, limit, &error_detail);
-
if (error_msg != nullptr) {
+ // We call this here so that we can try and generate a full error
+ // message with the overlapping mapping. There's no guarantee that
+ // that there will be an overlap though, since
+ // - The kernel is not *required* to honor expected_ptr unless MAP_FIXED is
+ // true, even if there is no overlap
+ // - There might have been an overlap at the point of mmap, but the
+ // overlapping region has since been unmapped.
+ std::string error_detail;
+ CheckNonOverlapping(expected, limit, &error_detail);
std::ostringstream os;
os << StringPrintf("Failed to mmap at expected address, mapped at "
"0x%08" PRIxPTR " instead of 0x%08" PRIxPTR,
@@ -338,11 +337,18 @@ MemMap* MemMap::MapAnonymous(const char* name,
saved_errno = errno;
if (actual == MAP_FAILED) {
- PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ if (error_msg != nullptr) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
- *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. See process "
- "maps in the log.", expected_ptr, page_aligned_byte_count, prot,
- flags, fd.get(), strerror(saved_errno));
+ *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. "
+ "See process maps in the log.",
+ expected_ptr,
+ page_aligned_byte_count,
+ prot,
+ flags,
+ fd.get(),
+ strerror(saved_errno));
+ }
return nullptr;
}
std::ostringstream check_map_request_error_msg;
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index 81c855e736..e703b78cfa 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -164,6 +164,19 @@ TEST_F(MemMapTest, MapAnonymousEmpty) {
ASSERT_TRUE(error_msg.empty());
}
+TEST_F(MemMapTest, MapAnonymousFailNullError) {
+ CommonInit();
+ // Test that we don't crash with a null error_str when mapping at an invalid location.
+ std::unique_ptr<MemMap> map(MemMap::MapAnonymous("MapAnonymousInvalid",
+ reinterpret_cast<uint8_t*>(kPageSize),
+ 0x20000,
+ PROT_READ | PROT_WRITE,
+ false,
+ false,
+ nullptr));
+ ASSERT_EQ(nullptr, map.get());
+}
+
#ifdef __LP64__
TEST_F(MemMapTest, MapAnonymousEmpty32bit) {
CommonInit();
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 422832e03c..19584edf7f 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -502,7 +502,7 @@ inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* metho
if (method->IsDirect()) {
return method;
}
- if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) {
+ if (method->GetDeclaringClass()->IsInterface() && !method->IsCopied()) {
return FindVirtualMethodForInterface(method, pointer_size);
}
return FindVirtualMethodForVirtual(method, pointer_size);
@@ -532,8 +532,9 @@ inline LengthPrefixedArray<ArtField>* Class::GetIFieldsPtr() {
return GetFieldPtr<LengthPrefixedArray<ArtField>*>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_));
}
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() {
- Class* super_class = GetSuperClass();
+ Class* super_class = GetSuperClass<kVerifyFlags, kReadBarrierOption>();
return (super_class != nullptr)
? MemberOffset(RoundUp(super_class->GetObjectSize(),
sizeof(mirror::HeapReference<mirror::Object>)))
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index cdc6204665..9190e44144 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1048,5 +1048,11 @@ uint32_t Class::Depth() {
return depth;
}
+uint32_t Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) {
+ std::string temp;
+ const DexFile::TypeId* type_id = dex_file.FindTypeId(GetDescriptor(&temp));
+ return (type_id == nullptr) ? DexFile::kDexNoIndex : dex_file.GetIndexForTypeId(*type_id);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 388a231cdc..6e3463c25c 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1006,6 +1006,8 @@ class MANAGED Class FINAL : public Object {
SHARED_REQUIRES(Locks::mutator_lock_);
// Get the offset of the first reference instance field. Other reference instance fields follow.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
MemberOffset GetFirstReferenceInstanceFieldOffset()
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1119,6 +1121,9 @@ class MANAGED Class FINAL : public Object {
SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx);
}
+ uint32_t FindTypeIndexInOtherDexFile(const DexFile& dex_file)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
static Class* GetJavaLangClass() SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(HasJavaLangClass());
return java_lang_Class_.Read();
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index eb391be406..76a36ac893 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -1068,7 +1068,7 @@ inline void Object::VisitFieldsReferences(uint32_t ref_offsets, const Visitor& v
MemberOffset field_offset = kIsStatic
? klass->GetFirstReferenceStaticFieldOffset<kVerifyFlags, kReadBarrierOption>(
Runtime::Current()->GetClassLinker()->GetImagePointerSize())
- : klass->GetFirstReferenceInstanceFieldOffset();
+ : klass->GetFirstReferenceInstanceFieldOffset<kVerifyFlags, kReadBarrierOption>();
for (size_t i = 0u; i < num_reference_fields; ++i) {
// TODO: Do a simpler check?
if (field_offset.Uint32Value() != ClassOffset().Uint32Value()) {
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index ed4c5fc76c..c31b22ee89 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -50,6 +50,11 @@ static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (de
// Used by a class to denote that the verifier has attempted to check it at least once.
static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only)
+// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
+// that it was copied from its declaring class into another class. All methods marked kAccMiranda
+// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
+// array of a concrete class will also have this bit set.
+static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime)
static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only)
static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index bcc2d33333..910163c787 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -40,6 +40,12 @@ class OatMethodOffsets;
class OatHeader;
class OatDexFile;
+namespace gc {
+namespace collector {
+class DummyOatFile;
+} // namespace collector
+} // namespace gc
+
class OatFile {
public:
typedef art::OatDexFile OatDexFile;
@@ -312,6 +318,7 @@ class OatFile {
// elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't.
mutable std::list<std::string> string_cache_ GUARDED_BY(secondary_lookup_lock_);
+ friend class gc::collector::DummyOatFile; // For modifying begin_ and end_.
friend class OatClass;
friend class art::OatDexFile;
friend class OatDumper; // For GetBase and GetLimit
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index f9d916a92e..d64aa432fc 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -394,6 +394,7 @@ bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options,
// Intended for local changes only.
static void MaybeOverrideVerbosity() {
// gLogVerbosity.class_linker = true; // TODO: don't check this in!
+ // gLogVerbosity.collector = true; // TODO: don't check this in!
// gLogVerbosity.compiler = true; // TODO: don't check this in!
// gLogVerbosity.deopt = true; // TODO: don't check this in!
// gLogVerbosity.gc = true; // TODO: don't check this in!
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index fe6a529078..6317f5e401 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -289,13 +289,18 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
stacked_shadow_frame_pushed_(false),
single_frame_deopt_(single_frame),
single_frame_done_(false),
- single_frame_deopt_method_(nullptr) {
+ single_frame_deopt_method_(nullptr),
+ single_frame_deopt_quick_method_header_(nullptr) {
}
ArtMethod* GetSingleFrameDeoptMethod() const {
return single_frame_deopt_method_;
}
+ const OatQuickMethodHeader* GetSingleFrameDeoptQuickMethodHeader() const {
+ return single_frame_deopt_quick_method_header_;
+ }
+
bool VisitFrame() OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
ArtMethod* method = GetMethod();
@@ -367,6 +372,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
exception_handler_->SetHandlerQuickArg0(reinterpret_cast<uintptr_t>(method));
single_frame_done_ = true;
single_frame_deopt_method_ = method;
+ single_frame_deopt_quick_method_header_ = GetCurrentOatQuickMethodHeader();
}
return true;
}
@@ -601,6 +607,7 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor {
const bool single_frame_deopt_;
bool single_frame_done_;
ArtMethod* single_frame_deopt_method_;
+ const OatQuickMethodHeader* single_frame_deopt_quick_method_header_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
};
@@ -634,7 +641,7 @@ void QuickExceptionHandler::DeoptimizeSingleFrame() {
DCHECK(deopt_method != nullptr);
if (Runtime::Current()->UseJit()) {
Runtime::Current()->GetJit()->GetCodeCache()->InvalidateCompiledCodeFor(
- deopt_method, handler_method_header_);
+ deopt_method, visitor.GetSingleFrameDeoptQuickMethodHeader());
} else {
// Transfer the code to interpreter.
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2aeb7921ce..eb5455a4cd 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1300,6 +1300,10 @@ void Runtime::InitNativeMethods() {
VLOG(startup) << "Runtime::InitNativeMethods exiting";
}
+void Runtime::ReclaimArenaPoolMemory() {
+ arena_pool_->LockReclaimMemory();
+}
+
void Runtime::InitThreadGroups(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
ScopedJniEnvLocalRefState env_state(env);
@@ -1887,7 +1891,6 @@ void Runtime::CreateJit() {
std::string error_msg;
jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg));
if (jit_.get() != nullptr) {
- compiler_callbacks_ = jit_->GetCompilerCallbacks();
jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(),
jit_options_->GetWarmupThreshold(),
jit_options_->GetOsrThreshold());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index cbb3e89444..8aac4ce9b4 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -564,6 +564,9 @@ class Runtime {
const ArenaPool* GetArenaPool() const {
return arena_pool_.get();
}
+
+ void ReclaimArenaPoolMemory();
+
LinearAlloc* GetLinearAlloc() {
return linear_alloc_.get();
}
diff --git a/runtime/safe_map.h b/runtime/safe_map.h
index a8b48ee4dc..0e5b503e26 100644
--- a/runtime/safe_map.h
+++ b/runtime/safe_map.h
@@ -99,16 +99,16 @@ class SafeMap {
}
// Used to insert a new mapping at a known position for better performance.
- iterator PutBefore(iterator pos, const K& k, const V& v) {
+ iterator PutBefore(const_iterator pos, const K& k, const V& v) {
// Check that we're using the correct position and the key is not in the map.
DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first));
- DCHECK(pos == map_.begin() || map_.key_comp()((--iterator(pos))->first, k));
+ DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k));
return map_.emplace_hint(pos, k, v);
}
- iterator PutBefore(iterator pos, const K& k, V&& v) {
+ iterator PutBefore(const_iterator pos, const K& k, V&& v) {
// Check that we're using the correct position and the key is not in the map.
DCHECK(pos == map_.end() || map_.key_comp()(k, pos->first));
- DCHECK(pos == map_.begin() || map_.key_comp()((--iterator(pos))->first, k));
+ DCHECK(pos == map_.begin() || map_.key_comp()((--const_iterator(pos))->first, k));
return map_.emplace_hint(pos, k, std::move(v));
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 2726e91130..97c47e1490 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -852,6 +852,22 @@ class Thread {
tls32_.weak_ref_access_enabled = enabled;
}
+ uint32_t GetDisableThreadFlipCount() const {
+ CHECK(kUseReadBarrier);
+ return tls32_.disable_thread_flip_count;
+ }
+
+ void IncrementDisableThreadFlipCount() {
+ CHECK(kUseReadBarrier);
+ ++tls32_.disable_thread_flip_count;
+ }
+
+ void DecrementDisableThreadFlipCount() {
+ CHECK(kUseReadBarrier);
+ DCHECK_GT(tls32_.disable_thread_flip_count, 0U);
+ --tls32_.disable_thread_flip_count;
+ }
+
// Activates single step control for debugging. The thread takes the
// ownership of the given SingleStepControl*. It is deleted by a call
// to DeactivateSingleStepControl or upon thread destruction.
@@ -1214,7 +1230,8 @@ class Thread {
daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
thread_exit_check_count(0), handling_signal_(false),
suspended_at_suspend_check(false), ready_for_debug_invoke(false),
- debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true) {
+ debug_method_entry_(false), is_gc_marking(false), weak_ref_access_enabled(true),
+ disable_thread_flip_count(0) {
}
union StateAndFlags state_and_flags;
@@ -1281,6 +1298,11 @@ class Thread {
// pause, this is not an issue.) Other collectors use Runtime::DisallowNewSystemWeaks() and
// ReferenceProcessor::EnableSlowPath().
bool32_t weak_ref_access_enabled;
+
+ // A thread local version of Heap::disable_thread_flip_count_. This keeps track of how many
+ // levels of (nested) JNI critical sections the thread is in and is used to detect a nested JNI
+ // critical section enter.
+ uint32_t disable_thread_flip_count;
} tls32_;
struct PACKED(8) tls_64bit_sized_values {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 07f94c0766..13564a6a0f 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1392,9 +1392,8 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is
return filename;
}
-bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) {
const std::string command_line(Join(arg_vector, ' '));
-
CHECK_GE(arg_vector.size(), 1U) << command_line;
// Convert the args to char pointers.
@@ -1417,7 +1416,6 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
setpgid(0, 0);
execv(program, &args[0]);
-
PLOG(ERROR) << "Failed to execv(" << command_line << ")";
// _exit to avoid atexit handlers in child.
_exit(1);
@@ -1425,23 +1423,32 @@ bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
if (pid == -1) {
*error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
command_line.c_str(), strerror(errno));
- return false;
+ return -1;
}
// wait for subprocess to finish
- int status;
+ int status = -1;
pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
if (got_pid != pid) {
*error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
"wanted %d, got %d: %s",
command_line.c_str(), pid, got_pid, strerror(errno));
- return false;
+ return -1;
}
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
- command_line.c_str());
- return false;
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
}
+ return -1;
+ }
+}
+
+bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+ int status = ExecAndReturnCode(arg_vector, error_msg);
+ if (status != 0) {
+ const std::string command_line(Join(arg_vector, ' '));
+ *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
+ command_line.c_str());
+ return false;
}
return true;
}
diff --git a/runtime/utils.h b/runtime/utils.h
index 79e4da19c8..83ac0b870e 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -292,6 +292,7 @@ std::string GetSystemImageFilename(const char* location, InstructionSet isa);
// Wrapper on fork/execv to run a command in a subprocess.
bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
+int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg);
// Returns true if the file exists.
bool FileExists(const std::string& filename);
@@ -348,7 +349,7 @@ static void ParseUintOption(const StringPiece& option,
UsageFn Usage,
bool is_long_option = true) {
std::string option_prefix = option_name + (is_long_option ? "=" : "");
- DCHECK(option.starts_with(option_prefix));
+ DCHECK(option.starts_with(option_prefix)) << option << " " << option_prefix;
const char* value_string = option.substr(option_prefix.size()).data();
int64_t parsed_integer_value = 0;
if (!ParseInt(value_string, &parsed_integer_value)) {
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index 86ab37e1e5..155c6ae5f3 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -28,3 +28,30 @@ Subclass.<init>
RUNNING sub object, sub class, sub nonstatic
Subclass.nonstaticMethod
PASSED sub object, sub class, sub nonstatic
+Calling method ConcreteClass->JniCallNonOverridenDefaultMethod on object of type ConcreteClass
+DefaultInterface.JniCallNonOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenDefaultMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenDefaultMethodWithSuper on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethodWithSuper
+DefaultInterface.JniCallOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenAbstractMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenAbstractMethod
+Calling method ConcreteClass->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+Calling method ConcreteClass->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method DefaultInterface->JniCallNonOverridenDefaultMethod on object of type ConcreteClass
+DefaultInterface.JniCallNonOverridenDefaultMethod
+Calling method DefaultInterface->JniCallOverridenDefaultMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethod
+Calling method DefaultInterface->JniCallOverridenAbstractMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenAbstractMethod
+Calling method DefaultInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+Calling method DefaultInterface->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method AbstractInterface->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index be7888b04a..f632331fe3 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -639,3 +639,85 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testNewStringObject(JNIEnv* env, jcl
extern "C" JNIEXPORT jlong JNICALL Java_Main_testGetMethodID(JNIEnv* env, jclass, jclass c) {
return reinterpret_cast<jlong>(env->GetMethodID(c, "a", "()V"));
}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_enterJniCriticalSection(JNIEnv* env, jclass,
+ jint arraySize,
+ jbyteArray array0,
+ jbyteArray array1) {
+ for (int i = 0; i < 50000; ++i) {
+ char* data0 = reinterpret_cast<char*>(env->GetPrimitiveArrayCritical(array0, nullptr));
+ char* data1 = reinterpret_cast<char*>(env->GetPrimitiveArrayCritical(array1, nullptr));
+ bool up = i % 2 == 0;
+ for (int j = 0; j < arraySize; ++j) {
+ if (up) {
+ data1[j] = data0[j] + 1;
+ } else {
+ data0[j] = data1[j] + 1;
+ }
+ }
+ env->ReleasePrimitiveArrayCritical(array1, data1, 0);
+ env->ReleasePrimitiveArrayCritical(array0, data0, 0);
+ }
+}
+
+class JniCallDefaultMethodsTest {
+ public:
+ explicit JniCallDefaultMethodsTest(JNIEnv* env)
+ : env_(env), concrete_class_(env_->FindClass("ConcreteClass")) {
+ assert(!env_->ExceptionCheck());
+ assert(concrete_class_ != nullptr);
+ }
+
+ void Test() {
+ TestCalls("ConcreteClass", { "JniCallNonOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethodWithSuper",
+ "JniCallOverridenAbstractMethod",
+ "JniCallConflictDefaultMethod",
+ "JniCallSoftConflictMethod" });
+ TestCalls("DefaultInterface", { "JniCallNonOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethod",
+ "JniCallOverridenAbstractMethod",
+ "JniCallConflictDefaultMethod",
+ "JniCallSoftConflictMethod" });
+ TestCalls("AbstractInterface", { "JniCallSoftConflictMethod" });
+ TestCalls("ConflictInterface", { "JniCallConflictDefaultMethod" });
+ }
+
+ private:
+ void TestCalls(const char* declaring_class, std::vector<const char*> methods) {
+ jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V");
+ jobject obj = env_->NewObject(concrete_class_, new_method);
+ assert(!env_->ExceptionCheck());
+ assert(obj != nullptr);
+ jclass decl_class = env_->FindClass(declaring_class);
+ assert(!env_->ExceptionCheck());
+ assert(decl_class != nullptr);
+ for (const char* method : methods) {
+ jmethodID method_id = env_->GetMethodID(decl_class, method, "()V");
+ assert(!env_->ExceptionCheck());
+ printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method);
+ env_->CallVoidMethod(obj, method_id);
+ if (env_->ExceptionCheck()) {
+ jthrowable thrown = env_->ExceptionOccurred();
+ env_->ExceptionClear();
+ jmethodID to_string = env_->GetMethodID(
+ env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;");
+ jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string);
+ assert(!env_->ExceptionCheck());
+ const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr);
+ assert(!env_->ExceptionCheck());
+ assert(exception_string_utf8 != nullptr);
+ printf("EXCEPTION OCCURED: %s\n", exception_string_utf8);
+ env_->ReleaseStringUTFChars(exception_string, exception_string_utf8);
+ }
+ }
+ }
+
+ JNIEnv* env_;
+ jclass concrete_class_;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) {
+ JniCallDefaultMethodsTest(env).Test();
+}
diff --git a/test/004-JniTest/smali/AbstractInterface.smali b/test/004-JniTest/smali/AbstractInterface.smali
new file mode 100644
index 0000000000..52b2fc537e
--- /dev/null
+++ b/test/004-JniTest/smali/AbstractInterface.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public interface LAbstractInterface;
+.super Ljava/lang/Object;
+
+# public interface AbstractInterface {
+# public void JniCallSoftConflictMethod();
+# }
+
+.method public abstract JniCallSoftConflictMethod()V
+.end method
+
diff --git a/test/004-JniTest/smali/ConcreteClass.smali b/test/004-JniTest/smali/ConcreteClass.smali
new file mode 100644
index 0000000000..a9c072fc2f
--- /dev/null
+++ b/test/004-JniTest/smali/ConcreteClass.smali
@@ -0,0 +1,72 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LConcreteClass;
+.super Ljava/lang/Object;
+.implements LDefaultInterface;
+.implements LConflictInterface;
+.implements LAbstractInterface;
+
+# public class ConcreteClass implements DefaultInterface, ConflictInterface, AbstractInterface {
+# public void JniCallOverridenAbstractMethod() {
+# System.out.println("ConcreteClass.JniCallOverridenAbstractMethod");
+# }
+#
+# public void JniCallOverridenDefaultMethod() {
+# System.out.println("ConcreteClass.JniCallOverridenDefaultMethod");
+# }
+#
+# public void JniCallOverridenDefaultMethodWithSuper() {
+# System.out.println("ConcreteClass.JniCallOverridenDefaultMethodWithSuper");
+# DefaultInterface.super.JniCallOverridenDefaultMethod();
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public JniCallOverridenAbstractMethod()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenAbstractMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethodWithSuper()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenDefaultMethodWithSuper"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-super {p0}, LDefaultInterface;->JniCallOverridenDefaultMethod()V
+
+ return-void
+.end method
diff --git a/test/004-JniTest/smali/ConflictInterface.smali b/test/004-JniTest/smali/ConflictInterface.smali
new file mode 100644
index 0000000000..fc3d474df0
--- /dev/null
+++ b/test/004-JniTest/smali/ConflictInterface.smali
@@ -0,0 +1,35 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public interface LConflictInterface;
+.super Ljava/lang/Object;
+
+# public interface ConflictInterface {
+# public default void JniCallConflictDefaultMethod() {
+# System.out.println("ConflictInterface.JniCallConflictDefaultMethod");
+# }
+#
+# }
+
+.method public JniCallConflictDefaultMethod()V
+ .locals 2
+
+ const-string v0, "ConflictInterface.JniCallConflictDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
diff --git a/test/004-JniTest/smali/DefaultInterface.smali b/test/004-JniTest/smali/DefaultInterface.smali
new file mode 100644
index 0000000000..1ee872154b
--- /dev/null
+++ b/test/004-JniTest/smali/DefaultInterface.smali
@@ -0,0 +1,77 @@
+# /*
+# * Copyright 2016 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public interface LDefaultInterface;
+.super Ljava/lang/Object;
+
+# public interface DefaultInterface {
+# public default void JniCallNonOverridenDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallNonOverridenDefaultMethod");
+# }
+#
+# public default void JniCallOverridenDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallOverridenDefaultMethod");
+# }
+#
+# public void JniCallOverridenAbstractMethod();
+#
+# public default void JniCallConflictDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallConflictDefaultMethod");
+# }
+#
+# public default void JniCallSoftConflictMethod() {
+# System.out.println("DefaultInterface.JniCallSoftConflictMethod");
+# }
+# }
+
+.method public JniCallNonOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallNonOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public abstract JniCallOverridenAbstractMethod()V
+.end method
+
+.method public JniCallConflictDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallConflictDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallSoftConflictMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallSoftConflictMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index ee3a3b9830..9f4a8522e7 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -38,8 +38,12 @@ public class Main {
testNewStringObject();
testRemoveLocalObject();
testProxyGetMethodID();
+ testJniCriticalSectionAndGc();
+ testCallDefaultMethods();
}
+ private static native void testCallDefaultMethods();
+
private static native void testFindClassOnAttachedNativeThread();
private static boolean testFindFieldOnAttachedNativeThreadField;
@@ -120,7 +124,7 @@ public class Main {
private static void testRemoveLocalObject() {
removeLocalObject(new Object());
}
-
+
private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);
@@ -222,6 +226,35 @@ public class Main {
}
private static native long testGetMethodID(Class<?> c);
+
+ // Exercise GC and JNI critical sections in parallel.
+ private static void testJniCriticalSectionAndGc() {
+ Thread runGcThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < 10; ++i) {
+ Runtime.getRuntime().gc();
+ }
+ }
+ });
+ Thread jniCriticalThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final int arraySize = 32;
+ byte[] array0 = new byte[arraySize];
+ byte[] array1 = new byte[arraySize];
+ enterJniCriticalSection(arraySize, array0, array1);
+ }
+ });
+ jniCriticalThread.start();
+ runGcThread.start();
+ try {
+ jniCriticalThread.join();
+ runGcThread.join();
+ } catch (InterruptedException ignored) {}
+ }
+
+ private static native void enterJniCriticalSection(int arraySize, byte[] array0, byte[] array);
}
class JniCallNonvirtualTest {
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index af25d9bc54..5b3fa14076 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -804,6 +804,7 @@ public class Main {
Assert.assertEquals(Math.round(-2.9d), -3l);
Assert.assertEquals(Math.round(-3.0d), -3l);
Assert.assertEquals(Math.round(0.49999999999999994d), 0l);
+ Assert.assertEquals(Math.round(9007199254740991.0d), 9007199254740991l); // 2^53 - 1
Assert.assertEquals(Math.round(Double.NaN), (long)+0.0d);
Assert.assertEquals(Math.round(Long.MAX_VALUE + 1.0d), Long.MAX_VALUE);
Assert.assertEquals(Math.round(Long.MIN_VALUE - 1.0d), Long.MIN_VALUE);
@@ -825,6 +826,9 @@ public class Main {
Assert.assertEquals(Math.round(-2.5f), -2);
Assert.assertEquals(Math.round(-2.9f), -3);
Assert.assertEquals(Math.round(-3.0f), -3);
+ // 0.4999999701976776123046875
+ Assert.assertEquals(Math.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f);
+ Assert.assertEquals(Math.round(16777215.0f), 16777215); // 2^24 - 1
Assert.assertEquals(Math.round(Float.NaN), (int)+0.0f);
Assert.assertEquals(Math.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE);
Assert.assertEquals(Math.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE);
@@ -1056,6 +1060,8 @@ public class Main {
Assert.assertEquals(StrictMath.round(-2.5f), -2);
Assert.assertEquals(StrictMath.round(-2.9f), -3);
Assert.assertEquals(StrictMath.round(-3.0f), -3);
+ // 0.4999999701976776123046875
+ Assert.assertEquals(StrictMath.round(Float.intBitsToFloat(0x3EFFFFFF)), (int)+0.0f);
Assert.assertEquals(StrictMath.round(Float.NaN), (int)+0.0f);
Assert.assertEquals(StrictMath.round(Integer.MAX_VALUE + 1.0f), Integer.MAX_VALUE);
Assert.assertEquals(StrictMath.round(Integer.MIN_VALUE - 1.0f), Integer.MIN_VALUE);
diff --git a/test/130-hprof/src-ex/Allocator.java b/test/130-hprof/src-ex/Allocator.java
new file mode 100644
index 0000000000..ee75a14f30
--- /dev/null
+++ b/test/130-hprof/src-ex/Allocator.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Simple allocator that returns a boot class path object.
+public class Allocator {
+ public static Object allocObject() {
+ return new Object();
+ }
+}
diff --git a/test/130-hprof/src/Main.java b/test/130-hprof/src/Main.java
index 67e52323dd..9868c617f5 100644
--- a/test/130-hprof/src/Main.java
+++ b/test/130-hprof/src/Main.java
@@ -16,6 +16,7 @@
import java.io.File;
import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
@@ -34,24 +35,21 @@ public class Main {
}
}
- public static void main(String[] args) {
- // Create some data.
- Object data[] = new Object[TEST_LENGTH];
- for (int i = 0; i < data.length; i++) {
- if (makeArray(i)) {
- data[i] = new Object[TEST_LENGTH];
- } else {
- data[i] = String.valueOf(i);
- }
+ private static Object allocInDifferentLoader() throws Exception {
+ final String DEX_FILE = System.getenv("DEX_LOCATION") + "/130-hprof-ex.jar";
+ Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Couldn't find path class loader class");
}
- for (int i = 0; i < data.length; i++) {
- if (makeArray(i)) {
- Object data2[] = (Object[]) data[i];
- fillArray(data, data2, i);
- }
- }
- System.out.println("Generated data.");
+ Constructor constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+ ClassLoader loader = (ClassLoader)constructor.newInstance(
+ DEX_FILE, ClassLoader.getSystemClassLoader());
+ Class allocator = loader.loadClass("Allocator");
+ return allocator.getDeclaredMethod("allocObject", null).invoke(null);
+ }
+ private static void createDumpAndConv() throws RuntimeException {
File dumpFile = null;
File convFile = null;
@@ -88,6 +86,43 @@ public class Main {
}
}
+ public static void main(String[] args) throws Exception {
+ // Create some data.
+ Object data[] = new Object[TEST_LENGTH];
+ for (int i = 0; i < data.length; i++) {
+ if (makeArray(i)) {
+ data[i] = new Object[TEST_LENGTH];
+ } else {
+ data[i] = String.valueOf(i);
+ }
+ }
+ for (int i = 0; i < data.length; i++) {
+ if (makeArray(i)) {
+ Object data2[] = (Object[]) data[i];
+ fillArray(data, data2, i);
+ }
+ }
+ System.out.println("Generated data.");
+
+ createDumpAndConv();
+ Class klass = Class.forName("org.apache.harmony.dalvik.ddmc.DdmVmInternal");
+ if (klass == null) {
+ throw new AssertionError("Couldn't find path class loader class");
+ }
+ Method enableMethod = klass.getDeclaredMethod("enableRecentAllocations",
+ Boolean.TYPE);
+ if (enableMethod == null) {
+ throw new AssertionError("Couldn't find path class loader class");
+ }
+ enableMethod.invoke(null, true);
+ Object o = allocInDifferentLoader();
+ // Run GC to cause class unloading.
+ Runtime.getRuntime().gc();
+ createDumpAndConv();
+ // TODO: Somehow check contents of hprof file.
+ enableMethod.invoke(null, false);
+ }
+
private static File getHprofConf() {
// Use the java.library.path. It points to the lib directory.
File libDir = new File(System.getProperty("java.library.path"));
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 31bb94cb8c..32bbc5b61d 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -391,6 +391,36 @@ public class Main {
array[base + 1] = 1;
}
+ /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (before)
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK: BoundsCheck
+ /// CHECK: ArraySet
+
+ /// CHECK-START: void Main.constantIndexing10(int[], int) BCE (after)
+ /// CHECK: Deoptimize
+ /// CHECK: Deoptimize
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK: ArraySet
+
+ static void constantIndexing10(int[] array, int base) {
+ // Offset hidden in incremented base.
+ array[base] = 1;
+ array[++base] = 2;
+ array[++base] = 3;
+ array[++base] = 4;
+ }
+
static void runAllConstantIndices() {
int[] a1 = { 0 };
int[] a6 = { 0, 0, 0, 0, 0, 0 };
@@ -502,6 +532,12 @@ public class Main {
a6[3] != 3 || a6[4] != 40 || a6[5] != 10) {
System.out.println("constant indices 9 failed!");
}
+
+ constantIndexing10(a6, 0);
+ if (a6[0] != 1 || a6[1] != 2 || a6[2] != 3 ||
+ a6[3] != 4 || a6[4] != 40 || a6[5] != 10) {
+ System.out.println("constant indices 10 failed!");
+ }
}
// A helper into which the actual throwing function should be inlined.
diff --git a/test/530-checker-loops/src/Main.java b/test/530-checker-loops/src/Main.java
index deff279f77..8633745a8a 100644
--- a/test/530-checker-loops/src/Main.java
+++ b/test/530-checker-loops/src/Main.java
@@ -471,7 +471,7 @@ public class Main {
//
/// CHECK-START: void Main.linearTriangularOnTwoArrayLengths(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
+ // TODO: also CHECK-NOT: Deoptimize, see b/27151190
private static void linearTriangularOnTwoArrayLengths(int n) {
int[] a = new int[n];
for (int i = 0; i < a.length; i++) {
@@ -513,7 +513,7 @@ public class Main {
//
/// CHECK-START: void Main.linearTriangularOnParameter(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
+ // TODO: also CHECK-NOT: Deoptimize, see b/27151190
private static void linearTriangularOnParameter(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++) {
@@ -528,22 +528,22 @@ public class Main {
}
}
- /// CHECK-START: void Main.linearTriangularVariations(int) BCE (before)
+ /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (before)
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
/// CHECK-DAG: BoundsCheck
//
- /// CHECK-START: void Main.linearTriangularVariations(int) BCE (after)
+ /// CHECK-START: void Main.linearTriangularVariationsInnerStrict(int) BCE (after)
/// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static void linearTriangularVariations(int n) {
+ // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ private static void linearTriangularVariationsInnerStrict(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
a[j] += 1;
}
- for (int j = i - 1; j >= 0; j--) {
+ for (int j = i - 1; j > -1; j--) {
a[j] += 1;
}
for (int j = i; j < n; j++) {
@@ -556,6 +556,34 @@ public class Main {
verifyTriangular(a);
}
+ /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.linearTriangularVariationsInnerNonStrict(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ private static void linearTriangularVariationsInnerNonStrict(int n) {
+ int[] a = new int[n];
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j <= i - 1; j++) {
+ a[j] += 1;
+ }
+ for (int j = i - 1; j >= 0; j--) {
+ a[j] += 1;
+ }
+ for (int j = i; j <= n - 1; j++) {
+ a[j] += 1;
+ }
+ for (int j = n - 1; j >= i; j--) {
+ a[j] += 1;
+ }
+ }
+ verifyTriangular(a);
+ }
+
// Verifier for triangular loops.
private static void verifyTriangular(int[] a, int[] b, int m, int n) {
expectEquals(n, a.length);
@@ -577,596 +605,6 @@ public class Main {
}
}
- /// CHECK-START: void Main.bubble(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.bubble(int[]) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static void bubble(int[] a) {
- for (int i = a.length; --i >= 0;) {
- for (int j = 0; j < i; j++) {
- if (a[j] > a[j+1]) {
- int tmp = a[j];
- a[j] = a[j+1];
- a[j+1] = tmp;
- }
- }
- }
- }
-
- /// CHECK-START: int Main.periodicIdiom(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.periodicIdiom(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int periodicIdiom(int tc) {
- int[] x = { 1, 3 };
- // Loop with periodic sequence (0, 1).
- int k = 0;
- int result = 0;
- for (int i = 0; i < tc; i++) {
- result += x[k];
- k = 1 - k;
- }
- return result;
- }
-
- /// CHECK-START: int Main.periodicSequence2(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.periodicSequence2(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int periodicSequence2(int tc) {
- int[] x = { 1, 3 };
- // Loop with periodic sequence (0, 1).
- int k = 0;
- int l = 1;
- int result = 0;
- for (int i = 0; i < tc; i++) {
- result += x[k];
- int t = l;
- l = k;
- k = t;
- }
- return result;
- }
-
- /// CHECK-START: int Main.periodicSequence4(int) BCE (before)
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.periodicSequence4(int) BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int periodicSequence4(int tc) {
- int[] x = { 1, 3, 5, 7 };
- // Loop with periodic sequence (0, 1, 2, 3).
- int k = 0;
- int l = 1;
- int m = 2;
- int n = 3;
- int result = 0;
- for (int i = 0; i < tc; i++) {
- result += x[k] + x[l] + x[m] + x[n]; // all used at once
- int t = n;
- n = k;
- k = l;
- l = m;
- m = t;
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightUp1() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightUp1() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightUp1() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightUp2() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightUp2() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightUp2() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
- result += x[i - Integer.MAX_VALUE + 10];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightUp3() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightUp3() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightUp3() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justOOBUp() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBUp() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBUp() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static int justOOBUp() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- // Infinite loop!
- for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightDown1() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightDown1() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightDown1() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightDown2() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightDown2() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightDown2() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
- result += x[Integer.MAX_VALUE + i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justRightDown3() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justRightDown3() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int justRightDown3() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.justOOBDown() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBDown() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int Main.justOOBDown() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static int justOOBDown() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int result = 0;
- // Infinite loop!
- for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: void Main.lowerOOB(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void lowerOOB(int[] x) {
- // OOB!
- for (int i = -1; i < x.length; i++) {
- sResult += x[i];
- }
- }
-
- /// CHECK-START: void Main.upperOOB(int[]) BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void upperOOB(int[] x) {
- // OOB!
- for (int i = 0; i <= x.length; i++) {
- sResult += x[i];
- }
- }
-
- /// CHECK-START: void Main.doWhileUpOOB() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void doWhileUpOOB() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int i = 0;
- // OOB!
- do {
- sResult += x[i++];
- } while (i <= x.length);
- }
-
- /// CHECK-START: void Main.doWhileDownOOB() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
- /// CHECK-NOT: Deoptimize
- private static void doWhileDownOOB() {
- int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
- int i = x.length - 1;
- // OOB!
- do {
- sResult += x[i--];
- } while (-1 <= i);
- }
-
- /// CHECK-START: int[] Main.multiply1() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.multiply1() BCE (after)
- /// CHECK-NOT: BoundsCheck
- /// CHECK-NOT: Deoptimize
- private static int[] multiply1() {
- int[] a = new int[10];
- try {
- for (int i = 0; i <= 3; i++) {
- for (int j = 0; j <= 3; j++) {
- // Range [0,9]: safe.
- a[i * j] += 1;
- }
- }
- } catch (Exception e) {
- a[0] += 1000;
- }
- return a;
- }
-
- /// CHECK-START: int[] Main.multiply2() BCE (before)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.multiply2() BCE (after)
- /// CHECK-DAG: BoundsCheck
- //
- /// CHECK-START: int[] Main.multiply2() BCE (after)
- /// CHECK-NOT: Deoptimize
- static int[] multiply2() {
- int[] a = new int[10];
- try {
- for (int i = -3; i <= 3; i++) {
- for (int j = -3; j <= 3; j++) {
- // Range [-9,9]: unsafe.
- a[i * j] += 1;
- }
- }
- } catch (Exception e) {
- a[0] += 1000;
- }
- return a;
- }
-
- /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int linearDynamicBCE1(int[] x, int lo, int hi) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- sResult += x[i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- sResult += x[offset + i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int wrapAroundDynamicBCE(int[] x) {
- int w = 9;
- int result = 0;
- for (int i = 0; i < 10; i++) {
- result += x[w];
- w = i;
- }
- return result;
- }
-
- /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- private static int periodicDynamicBCE(int[] x) {
- int k = 0;
- int result = 0;
- for (int i = 0; i < 10; i++) {
- result += x[k];
- k = 1 - k;
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
- // This loop could be infinite for hi = max int. Since i is also used
- // as subscript, however, dynamic bce can proceed.
- int result = 0;
- for (int i = lo; i <= hi; i++) {
- result += x[i];
- }
- return result;
- }
-
- /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
- /// CHECK-NOT: Deoptimize
- static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
- // As above, but now the index is not used as subscript,
- // and dynamic bce is not applied.
- int result = 0;
- for (int k = 0, i = lo; i <= hi; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
- /// CHECK-NOT: Deoptimize
- static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) {
- int result = 0;
- // Mix of int and long induction.
- int k = 0;
- for (long i = lo; i < hi; i++) {
- result += x[k++];
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before)
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
- // Order matters:
- /// CHECK: Deoptimize loop:<<Loop:B\d+>>
- // CHECK-NOT: Goto loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
- /// CHECK: Goto loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
- /// CHECK-DAG: Deoptimize loop:none
- static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) {
- // Deliberately test array length on a before the loop so that only bounds checks
- // on constant subscripts remain, making them a viable candidate for hoisting.
- if (a.length == 0) {
- return -1;
- }
- // Loop that allows BCE on x[i].
- int result = 0;
- for (int i = lo; i < hi; i++) {
- result += x[i];
- if ((i % 10) != 0) {
- // None of the subscripts inside a conditional are removed by dynamic bce,
- // making them a candidate for deoptimization based on constant indices.
- // Compiler should ensure the array loads are not subsequently hoisted
- // "above" the deoptimization "barrier" on the bounds.
- a[0][i] = 1;
- a[1][i] = 2;
- a[99][i] = 3;
- }
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- // For brevity, just test occurrence of at least one of each in the loop:
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-NOT: ArrayGet loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
- /// CHECK-NOT: NullCheck loop:{{B\d+}}
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
- /// CHECK-DAG: Deoptimize loop:none
- static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q,
- boolean[] r,
- byte[] s,
- char[] t,
- short[] u,
- int[] v,
- long[] w,
- float[] x,
- double[] y, int lo, int hi) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- // All constant index array references can be hoisted out of the loop during BCE on q[i].
- result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] +
- (int) w[0] + (int) x[0] + (int) y[0];
- }
- return result;
- }
-
- /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayGet loop:<<Loop>>
- /// CHECK-DAG: NullCheck loop:<<Loop>>
- /// CHECK-DAG: ArrayLength loop:<<Loop>>
- /// CHECK-DAG: BoundsCheck loop:<<Loop>>
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
- /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
- /// CHECK-DAG: Deoptimize loop:none
- //
- /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
- /// CHECK-NOT: ArrayLength loop:{{B\d+}}
- /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
- static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) {
- int result = 0;
- for (int i = lo; i < hi; i++) {
- // Similar to above, but now implicit call to intValue() may prevent hoisting
- // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i].
- result += q[i] + z[0];
- }
- return result;
- }
-
- //
- // Verifier.
- //
-
public static void main(String[] args) {
int[] empty = { };
int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
@@ -1239,191 +677,8 @@ public class Main {
linearTriangularOnTwoArrayLengths(10);
linearTriangularOnOneArrayLength(10);
linearTriangularOnParameter(10);
- linearTriangularVariations(10);
-
- // Sorting.
- int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
- bubble(sort);
- for (int i = 0; i < 10; i++) {
- expectEquals(sort[i], x[i]);
- }
-
- // Periodic adds (1, 3), one at the time.
- expectEquals(0, periodicIdiom(-1));
- for (int tc = 0; tc < 32; tc++) {
- int expected = (tc >> 1) << 2;
- if ((tc & 1) != 0)
- expected += 1;
- expectEquals(expected, periodicIdiom(tc));
- }
-
- // Periodic adds (1, 3), one at the time.
- expectEquals(0, periodicSequence2(-1));
- for (int tc = 0; tc < 32; tc++) {
- int expected = (tc >> 1) << 2;
- if ((tc & 1) != 0)
- expected += 1;
- expectEquals(expected, periodicSequence2(tc));
- }
-
- // Periodic adds (1, 3, 5, 7), all at once.
- expectEquals(0, periodicSequence4(-1));
- for (int tc = 0; tc < 32; tc++) {
- expectEquals(tc * 16, periodicSequence4(tc));
- }
-
- // Large bounds.
- expectEquals(55, justRightUp1());
- expectEquals(55, justRightUp2());
- expectEquals(55, justRightUp3());
- expectEquals(55, justRightDown1());
- expectEquals(55, justRightDown2());
- expectEquals(55, justRightDown3());
- sResult = 0;
- try {
- justOOBUp();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult = 1;
- }
- expectEquals(1, sResult);
- sResult = 0;
- try {
- justOOBDown();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult = 1;
- }
- expectEquals(1, sResult);
-
- // Lower bound goes OOB.
- sResult = 0;
- try {
- lowerOOB(x);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1000, sResult);
-
- // Upper bound goes OOB.
- sResult = 0;
- try {
- upperOOB(x);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Do while up goes OOB.
- sResult = 0;
- try {
- doWhileUpOOB();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Do while down goes OOB.
- sResult = 0;
- try {
- doWhileDownOOB();
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Multiplication.
- {
- int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 };
- int[] a1 = multiply1();
- for (int i = 0; i < 10; i++) {
- expectEquals(a1[i], e1[i]);
- }
- int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
- int[] a2 = multiply2();
- for (int i = 0; i < 10; i++) {
- expectEquals(a2[i], e2[i]);
- }
- }
-
- // Dynamic BCE.
- sResult = 0;
- try {
- linearDynamicBCE1(x, -1, x.length);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1000, sResult);
- sResult = 0;
- linearDynamicBCE1(x, 0, x.length);
- expectEquals(55, sResult);
- sResult = 0;
- try {
- linearDynamicBCE1(x, 0, x.length + 1);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1055, sResult);
-
- // Dynamic BCE with offset.
- sResult = 0;
- try {
- linearDynamicBCE2(x, 0, x.length, -1);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1000, sResult);
- sResult = 0;
- linearDynamicBCE2(x, 0, x.length, 0);
- expectEquals(55, sResult);
- sResult = 0;
- try {
- linearDynamicBCE2(x, 0, x.length, 1);
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult += 1000;
- }
- expectEquals(1054, sResult);
-
- // Dynamic BCE candidates.
- expectEquals(55, wrapAroundDynamicBCE(x));
- expectEquals(15, periodicDynamicBCE(x));
- expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9));
- expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9));
- expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10));
-
- // Dynamic BCE combined with constant indices.
- int[][] a;
- a = new int[0][0];
- expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10));
- a = new int[100][10];
- expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
- for (int i = 0; i < 10; i++) {
- expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]);
- expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
- expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
- }
- a = new int[2][10];
- sResult = 0;
- try {
- expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
- } catch (ArrayIndexOutOfBoundsException e) {
- sResult = 1;
- }
- expectEquals(1, sResult);
- expectEquals(a[0][1], 1);
- expectEquals(a[1][1], 2);
-
- // Dynamic BCE combined with constant indices of all types.
- boolean[] x1 = { true };
- byte[] x2 = { 2 };
- char[] x3 = { 3 };
- short[] x4 = { 4 };
- int[] x5 = { 5 };
- long[] x6 = { 6 };
- float[] x7 = { 7 };
- double[] x8 = { 8 };
- expectEquals(415,
- dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
- Integer[] x9 = { 9 };
- expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
+ linearTriangularVariationsInnerStrict(10);
+ linearTriangularVariationsInnerNonStrict(10);
}
private static void expectEquals(int expected, int result) {
diff --git a/test/530-checker-loops2/expected.txt b/test/530-checker-loops2/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/530-checker-loops2/expected.txt
diff --git a/test/530-checker-loops2/info.txt b/test/530-checker-loops2/info.txt
new file mode 100644
index 0000000000..f5d334d011
--- /dev/null
+++ b/test/530-checker-loops2/info.txt
@@ -0,0 +1 @@
+Test on loop optimizations.
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
new file mode 100644
index 0000000000..64be1a2be4
--- /dev/null
+++ b/test/530-checker-loops2/src/Main.java
@@ -0,0 +1,999 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Test on loop optimizations.
+//
+public class Main {
+
+ static int sResult;
+
+ //
+ // Various sequence variables used in bound checks.
+ //
+
+ /// CHECK-START: void Main.bubble(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.bubble(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ // TODO: also CHECK-NOT: Deoptimize, see b/27151190
+ private static void bubble(int[] a) {
+ for (int i = a.length; --i >= 0;) {
+ for (int j = 0; j < i; j++) {
+ if (a[j] > a[j+1]) {
+ int tmp = a[j];
+ a[j] = a[j+1];
+ a[j+1] = tmp;
+ }
+ }
+ }
+ }
+
+ /// CHECK-START: int Main.periodicIdiom(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicIdiom(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicIdiom(int tc) {
+ int[] x = { 1, 3 };
+ // Loop with periodic sequence (0, 1).
+ int k = 0;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k];
+ k = 1 - k;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.periodicSequence2(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicSequence2(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicSequence2(int tc) {
+ int[] x = { 1, 3 };
+ // Loop with periodic sequence (0, 1).
+ int k = 0;
+ int l = 1;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k];
+ int t = l;
+ l = k;
+ k = t;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.periodicSequence4(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicSequence4(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicSequence4(int tc) {
+ int[] x = { 1, 3, 5, 7 };
+ // Loop with periodic sequence (0, 1, 2, 3).
+ int k = 0;
+ int l = 1;
+ int m = 2;
+ int n = 3;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k] + x[l] + x[m] + x[n]; // all used at once
+ int t = n;
+ n = k;
+ k = l;
+ l = m;
+ m = t;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightUp1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightUp1() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10, k = 0; i < Integer.MAX_VALUE; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightUp2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightUp2() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10; i < Integer.MAX_VALUE; i++) {
+ result += x[i - Integer.MAX_VALUE + 10];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightUp3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightUp3() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightUp3() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MAX_VALUE - 10, k = 0; i <= Integer.MAX_VALUE - 1; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justOOBUp() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBUp() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBUp() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static int justOOBUp() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Infinite loop!
+ for (int i = Integer.MAX_VALUE - 9, k = 0; i <= Integer.MAX_VALUE; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightDown1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightDown1() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10, k = 0; i > Integer.MIN_VALUE; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightDown2() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightDown2() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10; i > Integer.MIN_VALUE; i--) {
+ result += x[Integer.MAX_VALUE + i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justRightDown3() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justRightDown3() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int justRightDown3() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ for (int i = Integer.MIN_VALUE + 10, k = 0; i >= Integer.MIN_VALUE + 1; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.justOOBDown() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBDown() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.justOOBDown() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static int justOOBDown() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int result = 0;
+ // Infinite loop!
+ for (int i = Integer.MIN_VALUE + 9, k = 0; i >= Integer.MIN_VALUE; i--) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: void Main.lowerOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.lowerOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void lowerOOB(int[] x) {
+ // OOB!
+ for (int i = -1; i < x.length; i++) {
+ sResult += x[i];
+ }
+ }
+
+ /// CHECK-START: void Main.upperOOB(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.upperOOB(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void upperOOB(int[] x) {
+ // OOB!
+ for (int i = 0; i <= x.length; i++) {
+ sResult += x[i];
+ }
+ }
+
+ /// CHECK-START: void Main.doWhileUpOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileUpOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void doWhileUpOOB() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int i = 0;
+ // OOB!
+ do {
+ sResult += x[i++];
+ } while (i <= x.length);
+ }
+
+ /// CHECK-START: void Main.doWhileDownOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doWhileDownOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void doWhileDownOOB() {
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ int i = x.length - 1;
+ // OOB!
+ do {
+ sResult += x[i--];
+ } while (-1 <= i);
+ }
+
+ /// CHECK-START: void Main.hiddenOOB1(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenOOB1(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenOOB1(int lo) {
+ int[] a = { 1 } ;
+ for (int i = lo; i <= 10; i++) {
+ // Dangerous loop where careless static range analysis would yield strict upper bound
+ // on index j of 5. When, for instance, lo and thus i = -2147483648, the upper bound
+ // becomes really positive due to arithmetic wrap-around, causing OOB.
+ // Dynamic BCE is feasible though, since it checks the range.
+ for (int j = 4; j < i - 5; j++) {
+ sResult += a[j - 4];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.hiddenOOB2(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenOOB2(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenOOB2(int hi) {
+ int[] a = { 1 } ;
+ for (int i = 0; i < hi; i++) {
+ // Dangerous loop where careless static range analysis would yield strict lower bound
+ // on index j of 5. When, for instance, hi and thus i = 2147483647, the upper bound
+ // becomes really negative due to arithmetic wrap-around, causing OOB.
+ // Dynamic BCE is feasible though, since it checks the range.
+ for (int j = 6; j > i + 5; j--) {
+ sResult += a[j - 6];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenInfiniteOOB() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ private static void hiddenInfiniteOOB() {
+ int[] a = { 11 } ;
+ for (int i = -1; i <= 0; i++) {
+ // Dangerous loop where careless static range analysis would yield a safe upper bound
+ // of -3. In reality, due to arithmetic wrap-around (when i = -1, j <= 2147483647;
+ // whereas when i = 0, j <= -3), this is an infinite loop that goes OOB.
+ for (int j = -3; j <= 2147483646 * i - 3; j++) {
+ sResult += a[j + 3];
+ }
+ }
+ }
+
+ /// CHECK-START: void Main.hiddenFiniteOOB() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.hiddenFiniteOOB() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ private static void hiddenFiniteOOB() {
+ int[] a = { 111 } ;
+ for (int i = -1; i <= 0; i++) {
+ // Dangerous loop similar as above where the loop is now finite, but the
+ // loop still goes out of bounds for i = -1 due to the large upper bound.
+ // Dynamic BCE is feasible though, since it checks the range.
+ for (int j = -4; j < 2147483646 * i - 3; j++) {
+ sResult += a[j + 4];
+ }
+ }
+ }
+
+ /// CHECK-START: int[] Main.add() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.add() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int[] add() {
+ int[] a = new int[10];
+ for (int i = 0; i <= 3; i++) {
+ for (int j = 0; j <= 6; j++) {
+ a[i + j] += 1;
+ }
+ }
+ return a;
+ }
+
+ /// CHECK-START: int[] Main.multiply1() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.multiply1() BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int[] multiply1() {
+ int[] a = new int[10];
+ try {
+ for (int i = 0; i <= 3; i++) {
+ for (int j = 0; j <= 3; j++) {
+ // Range [0,9]: safe.
+ a[i * j] += 1;
+ }
+ }
+ } catch (Exception e) {
+ a[0] += 1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int[] Main.multiply2() BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.multiply2() BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int[] Main.multiply2() BCE (after)
+ /// CHECK-NOT: Deoptimize
+ static int[] multiply2() {
+ int[] a = new int[10];
+ try {
+ for (int i = -3; i <= 3; i++) {
+ for (int j = -3; j <= 3; j++) {
+ // Range [-9,9]: unsafe.
+ a[i * j] += 1;
+ }
+ }
+ } catch (Exception e) {
+ a[0] += 1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.linearDynamicBCE1(int[], int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int linearDynamicBCE1(int[] x, int lo, int hi) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ sResult += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.linearDynamicBCE2(int[], int, int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int linearDynamicBCE2(int[] x, int lo, int hi, int offset) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ sResult += x[offset + i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.wrapAroundDynamicBCE(int[]) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int wrapAroundDynamicBCE(int[] x) {
+ int w = 9;
+ int result = 0;
+ for (int i = 0; i < 10; i++) {
+ result += x[w];
+ w = i;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.periodicDynamicBCE(int[]) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ private static int periodicDynamicBCE(int[] x) {
+ int k = 0;
+ int result = 0;
+ for (int i = 0; i < 10; i++) {
+ result += x[k];
+ k = 1 - k;
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:{{B\d+}}
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.dynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ static int dynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
+ // This loop could be infinite for hi = max int. Since i is also used
+ // as subscript, however, dynamic bce can proceed.
+ int result = 0;
+ for (int i = lo; i <= hi; i++) {
+ result += x[i];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEPossiblyInfiniteLoop(int[], int, int) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ static int noDynamicBCEPossiblyInfiniteLoop(int[] x, int lo, int hi) {
+ // As above, but now the index is not used as subscript,
+ // and dynamic bce is not applied.
+ int result = 0;
+ for (int k = 0, i = lo; i <= hi; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.noDynamicBCEMixedInductionTypes(int[], long, long) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ static int noDynamicBCEMixedInductionTypes(int[] x, long lo, long hi) {
+ int result = 0;
+ // Mix of int and long induction.
+ int k = 0;
+ for (long i = lo; i < hi; i++) {
+ result += x[k++];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:<<InnerLoop:B\d+>>
+ /// CHECK-DAG: ArrayGet loop:<<InnerLoop>>
+ /// CHECK-DAG: If loop:<<InnerLoop>>
+ /// CHECK-DAG: If loop:<<OuterLoop:B\d+>>
+ /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
+ //
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<InnerLoop:B\d+>>
+ /// CHECK-DAG: Deoptimize loop:<<OuterLoop:B\d+>>
+ /// CHECK-EVAL: "<<InnerLoop>>" != "<<OuterLoop>>"
+ //
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ //
+ // No additional top tests were introduced.
+ /// CHECK-START: int Main.dynamicBCEConstantRange(int[]) BCE (after)
+ /// CHECK-DAG: If
+ /// CHECK-DAG: If
+ /// CHECK-NOT: If
+ static int dynamicBCEConstantRange(int[] x) {
+ int result = 0;
+ for (int i = 2; i <= 6; i++) {
+ // Range analysis sees that innermost loop is finite and always taken.
+ for (int j = i - 2; j <= i + 2; j++) {
+ result += x[j];
+ }
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (before)
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
+ // Order matters:
+ /// CHECK: Deoptimize loop:<<Loop:B\d+>>
+ // CHECK-NOT: Goto loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: {{l\d+}} ArrayGet loop:<<Loop>>
+ /// CHECK: Goto loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndices(int[], int[][], int, int) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ static int dynamicBCEAndConstantIndices(int[] x, int[][] a, int lo, int hi) {
+ // Deliberately test array length on a before the loop so that only bounds checks
+ // on constant subscripts remain, making them a viable candidate for hoisting.
+ if (a.length == 0) {
+ return -1;
+ }
+ // Loop that allows BCE on x[i].
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ result += x[i];
+ if ((i % 10) != 0) {
+ // None of the subscripts inside a conditional are removed by dynamic bce,
+ // making them a candidate for deoptimization based on constant indices.
+ // Compiler should ensure the array loads are not subsequently hoisted
+ // "above" the deoptimization "barrier" on the bounds.
+ a[0][i] = 1;
+ a[1][i] = 2;
+ a[99][i] = 3;
+ }
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ // For brevity, just test occurrence of at least one of each in the loop:
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-NOT: ArrayGet loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
+ /// CHECK-NOT: NullCheck loop:{{B\d+}}
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndicesAllPrimTypes(int[], boolean[], byte[], char[], short[], int[], long[], float[], double[], int, int) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ static int dynamicBCEAndConstantIndicesAllPrimTypes(int[] q,
+ boolean[] r,
+ byte[] s,
+ char[] t,
+ short[] u,
+ int[] v,
+ long[] w,
+ float[] x,
+ double[] y, int lo, int hi) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ // All constant index array references can be hoisted out of the loop during BCE on q[i].
+ result += q[i] + (r[0] ? 1 : 0) + (int) s[0] + (int) t[0] + (int) u[0] + (int) v[0] +
+ (int) w[0] + (int) x[0] + (int) y[0];
+ }
+ return result;
+ }
+
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (before)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayGet loop:<<Loop>>
+ /// CHECK-DAG: NullCheck loop:<<Loop>>
+ /// CHECK-DAG: ArrayLength loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
+ /// CHECK-DAG: ArrayGet loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Deoptimize loop:none
+ //
+ /// CHECK-START: int Main.dynamicBCEAndConstantIndexRefType(int[], java.lang.Integer[], int, int) BCE (after)
+ /// CHECK-NOT: ArrayLength loop:{{B\d+}}
+ /// CHECK-NOT: BoundsCheck loop:{{B\d+}}
+ static int dynamicBCEAndConstantIndexRefType(int[] q, Integer[] z, int lo, int hi) {
+ int result = 0;
+ for (int i = lo; i < hi; i++) {
+ // Similar to above, but now implicit call to intValue() may prevent hoisting
+ // z[0] itself during BCE on q[i]. Therefore, we just check BCE on q[i].
+ result += q[i] + z[0];
+ }
+ return result;
+ }
+
+ //
+ // Verifier.
+ //
+
+ public static void main(String[] args) {
+ // Set to run expensive tests for correctness too.
+ boolean HEAVY = false;
+
+ int[] x = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ // Sorting.
+ int[] sort = { 5, 4, 1, 9, 10, 2, 7, 6, 3, 8 };
+ bubble(sort);
+ for (int i = 0; i < 10; i++) {
+ expectEquals(sort[i], x[i]);
+ }
+
+ // Periodic adds (1, 3), one at the time.
+ expectEquals(0, periodicIdiom(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ int expected = (tc >> 1) << 2;
+ if ((tc & 1) != 0)
+ expected += 1;
+ expectEquals(expected, periodicIdiom(tc));
+ }
+
+ // Periodic adds (1, 3), one at the time.
+ expectEquals(0, periodicSequence2(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ int expected = (tc >> 1) << 2;
+ if ((tc & 1) != 0)
+ expected += 1;
+ expectEquals(expected, periodicSequence2(tc));
+ }
+
+ // Periodic adds (1, 3, 5, 7), all at once.
+ expectEquals(0, periodicSequence4(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ expectEquals(tc * 16, periodicSequence4(tc));
+ }
+
+ // Large bounds.
+ expectEquals(55, justRightUp1());
+ expectEquals(55, justRightUp2());
+ expectEquals(55, justRightUp3());
+ expectEquals(55, justRightDown1());
+ expectEquals(55, justRightDown2());
+ expectEquals(55, justRightDown3());
+ sResult = 0;
+ try {
+ justOOBUp();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+ sResult = 0;
+ try {
+ justOOBDown();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+
+ // Lower bound goes OOB.
+ sResult = 0;
+ try {
+ lowerOOB(x);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+
+ // Upper bound goes OOB.
+ sResult = 0;
+ try {
+ upperOOB(x);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Do while up goes OOB.
+ sResult = 0;
+ try {
+ doWhileUpOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Do while down goes OOB.
+ sResult = 0;
+ try {
+ doWhileDownOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Hidden OOB.
+ sResult = 0;
+ try {
+ hiddenOOB1(10); // no OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1, sResult);
+ sResult = 0;
+ try {
+ hiddenOOB1(-2147483648); // OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1001, sResult);
+ sResult = 0;
+ try {
+ hiddenOOB2(1); // no OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1, sResult);
+ if (HEAVY) {
+ sResult = 0;
+ try {
+ hiddenOOB2(2147483647); // OOB
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1002, sResult);
+ }
+ sResult = 0;
+ try {
+ hiddenInfiniteOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1011, sResult);
+ sResult = 0;
+ try {
+ hiddenFiniteOOB();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1111, sResult);
+
+ // Addition.
+ {
+ int[] e1 ={ 1, 2, 3, 4, 4, 4, 4, 3, 2, 1 };
+ int[] a1 = add();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a1[i], e1[i]);
+ }
+ }
+
+ // Multiplication.
+ {
+ int[] e1 = { 7, 1, 2, 2, 1, 0, 2, 0, 0, 1 };
+ int[] a1 = multiply1();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a1[i], e1[i]);
+ }
+ int[] e2 = { 1001, 0, 0, 1, 0, 0, 1, 0, 0, 1 };
+ int[] a2 = multiply2();
+ for (int i = 0; i < 10; i++) {
+ expectEquals(a2[i], e2[i]);
+ }
+ }
+
+ // Dynamic BCE.
+ sResult = 0;
+ try {
+ linearDynamicBCE1(x, -1, x.length);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ sResult = 0;
+ linearDynamicBCE1(x, 0, x.length);
+ expectEquals(55, sResult);
+ sResult = 0;
+ try {
+ linearDynamicBCE1(x, 0, x.length + 1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1055, sResult);
+
+ // Dynamic BCE with offset.
+ sResult = 0;
+ try {
+ linearDynamicBCE2(x, 0, x.length, -1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1000, sResult);
+ sResult = 0;
+ linearDynamicBCE2(x, 0, x.length, 0);
+ expectEquals(55, sResult);
+ sResult = 0;
+ try {
+ linearDynamicBCE2(x, 0, x.length, 1);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult += 1000;
+ }
+ expectEquals(1054, sResult);
+
+ // Dynamic BCE candidates.
+ expectEquals(55, wrapAroundDynamicBCE(x));
+ expectEquals(15, periodicDynamicBCE(x));
+ expectEquals(55, dynamicBCEPossiblyInfiniteLoop(x, 0, 9));
+ expectEquals(55, noDynamicBCEPossiblyInfiniteLoop(x, 0, 9));
+ expectEquals(55, noDynamicBCEMixedInductionTypes(x, 0, 10));
+ expectEquals(125, dynamicBCEConstantRange(x));
+
+ // Dynamic BCE combined with constant indices.
+ int[][] a;
+ a = new int[0][0];
+ expectEquals(-1, dynamicBCEAndConstantIndices(x, a, 0, 10));
+ a = new int[100][10];
+ expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
+ for (int i = 0; i < 10; i++) {
+ expectEquals((i % 10) != 0 ? 1 : 0, a[0][i]);
+ expectEquals((i % 10) != 0 ? 2 : 0, a[1][i]);
+ expectEquals((i % 10) != 0 ? 3 : 0, a[99][i]);
+ }
+ a = new int[2][10];
+ sResult = 0;
+ try {
+ expectEquals(55, dynamicBCEAndConstantIndices(x, a, 0, 10));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ sResult = 1;
+ }
+ expectEquals(1, sResult);
+ expectEquals(a[0][1], 1);
+ expectEquals(a[1][1], 2);
+
+ // Dynamic BCE combined with constant indices of all types.
+ boolean[] x1 = { true };
+ byte[] x2 = { 2 };
+ char[] x3 = { 3 };
+ short[] x4 = { 4 };
+ int[] x5 = { 5 };
+ long[] x6 = { 6 };
+ float[] x7 = { 7 };
+ double[] x8 = { 8 };
+ expectEquals(415,
+ dynamicBCEAndConstantIndicesAllPrimTypes(x, x1, x2, x3, x4, x5, x6, x7, x8, 0, 10));
+ Integer[] x9 = { 9 };
+ expectEquals(145, dynamicBCEAndConstantIndexRefType(x, x9, 0, 10));
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java
index 2d0688d57e..87a89bd9dc 100644
--- a/test/550-checker-multiply-accumulate/src/Main.java
+++ b/test/550-checker-multiply-accumulate/src/Main.java
@@ -1,18 +1,18 @@
/*
-* Copyright (C) 2015 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
public class Main {
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index 8d73d69db9..9c86154bd4 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -1,18 +1,18 @@
/*
-* Copyright (C) 2015 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
public class Main {
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index 41af97b3b7..2d70e111aa 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -1,18 +1,18 @@
/*
-* Copyright (C) 2016 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
public class Main {
diff --git a/test/576-polymorphic-inlining/expected.txt b/test/576-polymorphic-inlining/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/576-polymorphic-inlining/expected.txt
diff --git a/test/576-polymorphic-inlining/info.txt b/test/576-polymorphic-inlining/info.txt
new file mode 100644
index 0000000000..b3ef0c8fba
--- /dev/null
+++ b/test/576-polymorphic-inlining/info.txt
@@ -0,0 +1 @@
+Test for polymorphic inlining.
diff --git a/test/576-polymorphic-inlining/src/Main.java b/test/576-polymorphic-inlining/src/Main.java
new file mode 100644
index 0000000000..d8d09aff87
--- /dev/null
+++ b/test/576-polymorphic-inlining/src/Main.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ for (int i = 0; i < 20000; ++i) {
+ $noinline$testVoid(new Main());
+ $noinline$testVoid(new SubMain());
+ $noinline$testVoid(new SubSubMain());
+
+ $noinline$testWithReturnValue(new Main());
+ $noinline$testWithReturnValue(new SubMain());
+ $noinline$testWithReturnValue(new SubSubMain());
+
+ $noinline$testWithBackEdge(new Main());
+ $noinline$testWithBackEdge(new SubMain());
+ $noinline$testWithBackEdge(new SubSubMain());
+ }
+ }
+
+ public static void assertIdentical(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void $noinline$testVoid(Main m) {
+ if (doThrow) throw new Error("");
+ m.willInlineVoid();
+ m.willOnlyInlineForMainVoid();
+ }
+
+ public static void $noinline$testWithReturnValue(Main m) {
+ if (doThrow) throw new Error("");
+ assertIdentical(m.getClass(), m.willInlineWithReturnValue());
+ assertIdentical(m.getClass(), m.willOnlyInlineForMainWithReturnValue());
+ }
+
+ public static void $noinline$testWithBackEdge(Main m) {
+ if (doThrow) throw new Error("");
+ for (int i = 0; i < 10; ++i) {
+ m.willInlineVoid();
+ }
+ for (int i = 0; i < 10; ++i) {
+ m.willOnlyInlineForMainVoid();
+ }
+ }
+
+ public void willInlineVoid() {
+ }
+
+ public void willOnlyInlineForMainVoid() {
+ }
+
+ public Class willInlineWithReturnValue() {
+ return Main.class;
+ }
+
+ public Class willOnlyInlineForMainWithReturnValue() {
+ return Main.class;
+ }
+ public static boolean doThrow;
+}
+
+class SubMain extends Main {
+ public void willOnlyInlineForMainVoid() {
+ if (doThrow) throw new Error("");
+ }
+
+ public void willInlineVoid() {
+ }
+
+ public Class willInlineWithReturnValue() {
+ return SubMain.class;
+ }
+
+ public Class willOnlyInlineForMainWithReturnValue() {
+ return SubMain.class;
+ }
+}
+
+class SubSubMain extends SubMain {
+ public Class willInlineWithReturnValue() {
+ return SubSubMain.class;
+ }
+
+ public Class willOnlyInlineForMainWithReturnValue() {
+ return SubSubMain.class;
+ }
+}
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 44206df089..46100ae15c 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -208,13 +208,6 @@
"org.apache.harmony.tests.java.util.TimerTaskTest#test_scheduledExecutionTime"]
},
{
- description: "'cat -' does not work anymore",
- result: EXEC_FAILED,
- bug: 26395656,
- modes: [device],
- names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getOutputStream"]
-},
-{
description: "Missing resource in classpath",
result: EXEC_FAILED,
modes: [device],
@@ -272,5 +265,10 @@
"libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndNoSharedRegistry",
"libcore.util.NativeAllocationRegistryTest#testNativeAllocationNoAllocatorAndSharedRegistry",
"libcore.util.NativeAllocationRegistryTest#testNullArguments"]
+},
+{
+ description: "Only work with --mode=activity",
+ result: EXEC_FAILED,
+ names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ]
}
]
diff --git a/tools/libcore_failures_concurrent_collector.txt b/tools/libcore_failures_concurrent_collector.txt
index d8ef9baa64..19a61dc8cb 100644
--- a/tools/libcore_failures_concurrent_collector.txt
+++ b/tools/libcore_failures_concurrent_collector.txt
@@ -24,19 +24,6 @@
bug: 26155567
},
{
- description: "TimeoutException on host-{x86,x86-64}-concurrent-collector",
- result: EXEC_FAILED,
- modes: [host],
- names: ["libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushEnabled",
- "libcore.java.util.zip.DeflaterOutputStreamTest#testSyncFlushDisabled",
- "libcore.java.util.zip.GZIPOutputStreamTest#testSyncFlushEnabled",
- "libcore.java.util.zip.OldAndroidGZIPStreamTest#testGZIPStream",
- "libcore.java.util.zip.OldAndroidZipStreamTest#testZipStream",
- "libcore.java.util.zip.ZipFileTest#testZipFileWithLotsOfEntries",
- "libcore.java.util.zip.ZipInputStreamTest#testLongMessage"],
- bug: 26507762
-},
-{
description: "TimeoutException on hammerhead-concurrent-collector",
result: EXEC_FAILED,
modes: [device],
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index d5b89897e5..45b60dc647 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -34,6 +34,9 @@ adb shell getprop
echo -e "${green}Uptime${nc}"
adb shell uptime
+echo -e "${green}Battery info${nc}"
+adb shell dumpsys battery
+
echo -e "${green}Kill stalled dalvikvm processes${nc}"
processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
for i in $processes; do adb shell kill -9 $i; done
diff --git a/tools/symbolize-buildbot-crashes.sh b/tools/symbolize-buildbot-crashes.sh
new file mode 100755
index 0000000000..8dc4e27885
--- /dev/null
+++ b/tools/symbolize-buildbot-crashes.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# We push art and its dependencies to '/data/local/tmp', but the 'stack'
+# script expect things to be in '/'. So we just remove the
+# '/data/local/tmp' prefix.
+adb logcat -d | sed 's,/data/local/tmp,,g' | development/scripts/stack
+
+# Always return 0 to avoid having the buildbot complain about wrong stacks.
+exit 0