summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk21
-rw-r--r--OWNERS3
-rw-r--r--build/Android.gtest.mk6
-rw-r--r--build/art.go82
-rw-r--r--build/codegen.go8
-rw-r--r--compiler/Android.bp11
-rw-r--r--compiler/driver/compiler_driver-inl.h45
-rw-r--r--compiler/driver/compiler_driver.cc53
-rw-r--r--compiler/driver/compiler_driver.h22
-rw-r--r--compiler/driver/compiler_options.cc59
-rw-r--r--compiler/driver/compiler_options.h34
-rw-r--r--compiler/image_writer.cc36
-rw-r--r--compiler/jit/jit_compiler.cc32
-rw-r--r--compiler/linker/mips/relative_patcher_mips.cc67
-rw-r--r--compiler/linker/mips/relative_patcher_mips32r6_test.cc21
-rw-r--r--compiler/linker/mips/relative_patcher_mips_test.cc20
-rw-r--r--compiler/linker/mips64/relative_patcher_mips64.cc77
-rw-r--r--compiler/linker/mips64/relative_patcher_mips64_test.cc58
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h11
-rw-r--r--compiler/optimizing/code_generator_mips.cc440
-rw-r--r--compiler/optimizing/code_generator_mips.h68
-rw-r--r--compiler/optimizing/code_generator_mips64.cc249
-rw-r--r--compiler/optimizing/code_generator_mips64.h57
-rw-r--r--compiler/optimizing/code_generator_vector_arm_vixl.cc605
-rw-r--r--compiler/optimizing/induction_var_range.cc10
-rw-r--r--compiler/optimizing/induction_var_range.h1
-rw-r--r--compiler/optimizing/induction_var_range_test.cc12
-rw-r--r--compiler/optimizing/load_store_analysis.cc111
-rw-r--r--compiler/optimizing/load_store_analysis.h20
-rw-r--r--compiler/optimizing/load_store_analysis_test.cc194
-rw-r--r--compiler/optimizing/loop_optimization.cc308
-rw-r--r--compiler/optimizing/loop_optimization.h28
-rw-r--r--compiler/optimizing/nodes.h14
-rw-r--r--compiler/optimizing/nodes_vector.h50
-rw-r--r--compiler/optimizing/nodes_vector_test.cc309
-rw-r--r--compiler/optimizing/scheduler.cc221
-rw-r--r--compiler/optimizing/scheduler.h20
-rw-r--r--compiler/optimizing/scheduler_test.cc157
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc2
-rw-r--r--compiler/utils/mips/assembler_mips.cc108
-rw-r--r--compiler/utils/mips/assembler_mips.h47
-rw-r--r--compiler/utils/mips/assembler_mips32r5_test.cc541
-rw-r--r--compiler/utils/mips/assembler_mips32r6_test.cc236
-rw-r--r--dex2oat/dex2oat.cc8
-rw-r--r--dexdump/dexdump.cc56
-rw-r--r--dexlayout/dex_ir.cc6
-rw-r--r--dexlayout/dexlayout.h3
-rw-r--r--oatdump/oatdump.cc2
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/arch/arm/context_arm.h2
-rw-r--r--runtime/arch/arm64/context_arm64.h2
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S26
-rw-r--r--runtime/arch/x86/context_x86.h2
-rw-r--r--runtime/arch/x86_64/context_x86_64.h2
-rw-r--r--runtime/art_method.cc13
-rw-r--r--runtime/art_method.h6
-rw-r--r--runtime/class_linker.cc456
-rw-r--r--runtime/class_linker.h39
-rw-r--r--runtime/class_linker_test.cc106
-rw-r--r--runtime/common_runtime_test.cc59
-rw-r--r--runtime/common_runtime_test.h10
-rw-r--r--runtime/debugger.cc79
-rw-r--r--runtime/dex_file.h4
-rw-r--r--runtime/dex_file_verifier.cc4
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h27
-rw-r--r--runtime/entrypoints/entrypoint_utils.h5
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc4
-rw-r--r--runtime/gc/heap-inl.h9
-rw-r--r--runtime/gc/heap.cc8
-rw-r--r--runtime/gc/heap.h2
-rw-r--r--runtime/gc/heap_verification_test.cc5
-rw-r--r--runtime/gc/space/region_space-inl.h62
-rw-r--r--runtime/gc/space/region_space.cc95
-rw-r--r--runtime/gc/space/region_space.h44
-rw-r--r--runtime/gc/verification.cc54
-rw-r--r--runtime/gc/verification.h11
-rw-r--r--runtime/interpreter/interpreter_common.cc25
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc60
-rw-r--r--runtime/jit/profile_saver.cc59
-rw-r--r--runtime/mirror/class-inl.h53
-rw-r--r--runtime/mirror/class.h21
-rw-r--r--runtime/mirror/dex_cache_test.cc12
-rw-r--r--runtime/monitor.cc6
-rw-r--r--runtime/native/dalvik_system_VMStack.cc7
-rw-r--r--runtime/native/java_lang_Thread.cc7
-rw-r--r--runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc6
-rw-r--r--runtime/openjdkjvm/OpenjdkJvm.cc7
-rw-r--r--runtime/openjdkjvmti/Android.bp2
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc54
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h14
-rw-r--r--runtime/openjdkjvmti/events-inl.h27
-rw-r--r--runtime/openjdkjvmti/events.cc44
-rw-r--r--runtime/openjdkjvmti/jvmti_allocator.h10
-rw-r--r--runtime/openjdkjvmti/jvmti_weak_table.h7
-rw-r--r--runtime/openjdkjvmti/ti_allocator.cc89
-rw-r--r--runtime/openjdkjvmti/ti_allocator.h65
-rw-r--r--runtime/openjdkjvmti/ti_breakpoint.cc114
-rw-r--r--runtime/openjdkjvmti/ti_breakpoint.h94
-rw-r--r--runtime/openjdkjvmti/ti_class.cc14
-rw-r--r--runtime/openjdkjvmti/ti_method.cc34
-rw-r--r--runtime/openjdkjvmti/ti_method.h5
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc8
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h9
-rw-r--r--runtime/runtime_linux.cc11
-rw-r--r--runtime/suspend_reason.h37
-rw-r--r--runtime/thread-inl.h6
-rw-r--r--runtime/thread.cc62
-rw-r--r--runtime/thread.h5
-rw-r--r--runtime/thread_list.cc57
-rw-r--r--runtime/thread_list.h11
-rw-r--r--runtime/verifier/method_verifier.cc3
-rw-r--r--runtime/well_known_classes.cc2
-rw-r--r--runtime/well_known_classes.h1
-rw-r--r--runtime/zip_archive.cc2
-rw-r--r--runtime/zip_archive.h5
-rw-r--r--test/098-ddmc/src/Main.java60
-rw-r--r--test/141-class-unload/src/Main.java3
-rw-r--r--test/163-app-image-methods/expected.txt3
-rw-r--r--test/163-app-image-methods/info.txt3
-rw-r--r--test/163-app-image-methods/profile2
-rw-r--r--test/163-app-image-methods/run20
-rw-r--r--test/163-app-image-methods/src/AAA/Base.java22
-rw-r--r--test/163-app-image-methods/src/AAA/Derived.java21
-rw-r--r--test/163-app-image-methods/src/Main.java85
-rw-r--r--test/1900-track-alloc/alloc.cc159
-rw-r--r--test/1900-track-alloc/expected.txt (renamed from test/988-redefine-use-after-free/expected.txt)0
-rw-r--r--test/1900-track-alloc/info.txt1
-rwxr-xr-xtest/1900-track-alloc/run (renamed from test/988-redefine-use-after-free/run)0
-rw-r--r--test/1900-track-alloc/src/Main.java21
-rw-r--r--test/1900-track-alloc/src/art/Main.java32
-rw-r--r--test/1900-track-alloc/src/art/Test1900.java153
-rw-r--r--test/1901-get-bytecodes/bytecodes.cc63
-rw-r--r--test/1901-get-bytecodes/expected.txt0
-rw-r--r--test/1901-get-bytecodes/info.txt3
-rwxr-xr-xtest/1901-get-bytecodes/run17
-rw-r--r--test/1901-get-bytecodes/src/Main.java21
-rw-r--r--test/1901-get-bytecodes/src/art/Test1901.java147
-rwxr-xr-xtest/458-checker-instruct-simplification/build23
-rw-r--r--test/458-checker-instruct-simplification/smali/SmaliTests.smali141
-rw-r--r--test/458-checker-instruct-simplification/src/Main.java103
-rwxr-xr-xtest/536-checker-intrinsic-optimization/build23
-rw-r--r--test/536-checker-intrinsic-optimization/smali/SmaliTests.smali64
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java21
-rwxr-xr-xtest/565-checker-doublenegbitwise/build23
-rw-r--r--test/565-checker-doublenegbitwise/smali/SmaliTests.smali405
-rw-r--r--test/565-checker-doublenegbitwise/src/Main.java303
-rwxr-xr-xtest/586-checker-null-array-get/build23
-rw-r--r--test/586-checker-null-array-get/smali/SmaliTests.smali96
-rw-r--r--test/586-checker-null-array-get/src/Main.java27
-rwxr-xr-xtest/593-checker-boolean-2-integral-conv/build23
-rw-r--r--test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali119
-rw-r--r--test/593-checker-boolean-2-integral-conv/src/Main.java97
-rw-r--r--test/593-checker-shift-and-simplifier/expected.txt1
-rw-r--r--test/593-checker-shift-and-simplifier/smali/SmaliTests.smali58
-rw-r--r--test/593-checker-shift-and-simplifier/src/Main.java32
-rw-r--r--test/596-app-images/src/Main.java88
-rw-r--r--test/623-checker-loop-regressions/src/Main.java23
-rwxr-xr-xtest/633-checker-rtp-getclass/build23
-rw-r--r--test/633-checker-rtp-getclass/smali/SmaliTests.smali65
-rw-r--r--test/633-checker-rtp-getclass/src/Main.java41
-rw-r--r--test/640-checker-boolean-simd/src/Main.java24
-rw-r--r--test/640-checker-byte-simd/src/Main.java42
-rw-r--r--test/640-checker-char-simd/src/Main.java42
-rw-r--r--test/640-checker-int-simd/src/Main.java69
-rw-r--r--test/640-checker-short-simd/src/Main.java42
-rw-r--r--test/645-checker-abs-simd/src/Main.java36
-rw-r--r--test/646-checker-hadd-alt-byte/src/Main.java44
-rw-r--r--test/646-checker-hadd-alt-char/src/Main.java36
-rw-r--r--test/646-checker-hadd-alt-short/src/Main.java44
-rw-r--r--test/646-checker-hadd-byte/src/Main.java44
-rw-r--r--test/646-checker-hadd-char/src/Main.java44
-rw-r--r--test/646-checker-hadd-short/src/Main.java72
-rw-r--r--test/651-checker-byte-simd-minmax/src/Main.java28
-rw-r--r--test/651-checker-char-simd-minmax/src/Main.java14
-rw-r--r--test/651-checker-int-simd-minmax/src/Main.java14
-rw-r--r--test/651-checker-short-simd-minmax/src/Main.java28
-rwxr-xr-xtest/652-deopt-intrinsic/run18
-rw-r--r--test/656-checker-simd-opt/expected.txt1
-rw-r--r--test/656-checker-simd-opt/info.txt1
-rw-r--r--test/656-checker-simd-opt/src/Main.java112
-rw-r--r--test/658-fp-read-barrier/expected.txt0
-rw-r--r--test/658-fp-read-barrier/info.txt2
-rw-r--r--test/658-fp-read-barrier/src/Main.java138
-rw-r--r--test/706-checker-scheduler/src/Main.java290
-rw-r--r--test/952-invoke-custom-kinds/build22
-rw-r--r--test/952-invoke-custom-kinds/classes/Main.classbin0 -> 302 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/Interface.classbin0 -> 152 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.classbin0 -> 503 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.classbin0 -> 239 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.classbin0 -> 634 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.classbin0 -> 7055 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/Super.classbin0 -> 468 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.classbin0 -> 1124 bytes
-rw-r--r--test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.classbin0 -> 8641 bytes
-rw-r--r--test/952-invoke-custom-kinds/expected.txt38
-rw-r--r--test/952-invoke-custom-kinds/info.txt7
-rw-r--r--test/988-method-trace/check22
-rw-r--r--test/988-method-trace/expected.txt244
-rw-r--r--test/988-method-trace/expected_jack.diff10
-rwxr-xr-xtest/988-method-trace/gen_srcs.py321
-rw-r--r--test/988-method-trace/src/art/Test988.java78
-rw-r--r--test/988-method-trace/src/art/Test988Intrinsics.java135
-rw-r--r--test/988-method-trace/src/art/Trace.java11
-rw-r--r--test/989-method-trace-throw/src/art/Trace.java11
-rw-r--r--test/990-field-trace/src/art/Trace.java11
-rw-r--r--test/991-field-trace-2/src/art/Trace.java11
-rw-r--r--test/992-source-data/expected.txt2
-rw-r--r--test/992-source-data/src/art/Target2.java (renamed from test/992-source-data/src/art/Test2.java)2
-rw-r--r--test/992-source-data/src/art/Test992.java2
-rw-r--r--test/993-breakpoints/breakpoints.cc69
-rw-r--r--test/993-breakpoints/expected.txt613
-rw-r--r--test/993-breakpoints/info.txt7
-rwxr-xr-xtest/993-breakpoints/run18
-rw-r--r--test/993-breakpoints/src/Main.java21
-rw-r--r--test/993-breakpoints/src/art/Breakpoint.java202
-rw-r--r--test/993-breakpoints/src/art/Test993.java498
-rw-r--r--test/994-breakpoint-line/expected.txt34
-rw-r--r--test/994-breakpoint-line/info.txt5
-rwxr-xr-xtest/994-breakpoint-line/run18
-rw-r--r--test/994-breakpoint-line/src/Main.java21
-rw-r--r--test/994-breakpoint-line/src/art/Breakpoint.java202
-rw-r--r--test/994-breakpoint-line/src/art/Test994.java72
-rw-r--r--test/995-breakpoints-throw/expected.txt34
-rw-r--r--test/995-breakpoints-throw/info.txt6
-rwxr-xr-xtest/995-breakpoints-throw/run18
-rw-r--r--test/995-breakpoints-throw/src/Main.java21
-rw-r--r--test/995-breakpoints-throw/src/art/Breakpoint.java202
-rw-r--r--test/995-breakpoints-throw/src/art/Test995.java136
-rw-r--r--test/996-breakpoint-obsolete/expected.txt14
-rw-r--r--test/996-breakpoint-obsolete/info.txt4
-rw-r--r--test/996-breakpoint-obsolete/obsolete_breakpoints.cc76
-rwxr-xr-xtest/996-breakpoint-obsolete/run18
-rw-r--r--test/996-breakpoint-obsolete/src/Main.java21
-rw-r--r--test/996-breakpoint-obsolete/src/art/Breakpoint.java202
-rw-r--r--test/996-breakpoint-obsolete/src/art/Redefinition.java (renamed from test/988-redefine-use-after-free/src-ex/art/Redefinition.java)0
-rw-r--r--test/996-breakpoint-obsolete/src/art/Test996.java152
-rw-r--r--test/997-single-step/expected.txt12
-rw-r--r--test/997-single-step/info.txt3
-rwxr-xr-xtest/997-single-step/run18
-rw-r--r--test/997-single-step/src/Main.java21
-rw-r--r--test/997-single-step/src/art/Breakpoint.java202
-rw-r--r--test/997-single-step/src/art/Test997.java82
-rw-r--r--test/997-single-step/src/art/Trace.java56
-rw-r--r--test/998-redefine-use-after-free/expected.txt0
-rw-r--r--test/998-redefine-use-after-free/info.txt (renamed from test/988-redefine-use-after-free/info.txt)0
-rwxr-xr-xtest/998-redefine-use-after-free/run17
-rw-r--r--test/998-redefine-use-after-free/src-ex/DexCacheSmash.java (renamed from test/988-redefine-use-after-free/src-ex/DexCacheSmash.java)0
-rw-r--r--test/998-redefine-use-after-free/src-ex/art/Redefinition.java91
-rw-r--r--test/998-redefine-use-after-free/src/Main.java (renamed from test/988-redefine-use-after-free/src/Main.java)2
-rw-r--r--test/Android.bp8
-rw-r--r--test/Android.run-test-jvmti-java-library.mk159
-rw-r--r--test/ForClassLoaderA/Classes.java31
-rw-r--r--test/ForClassLoaderB/Classes.java30
-rw-r--r--test/ForClassLoaderC/Classes.java30
-rw-r--r--test/ForClassLoaderD/Classes.java27
-rw-r--r--test/dexdump/invoke-custom.dexbin3276 -> 8984 bytes
-rw-r--r--test/dexdump/invoke-custom.lst39
-rw-r--r--test/dexdump/invoke-custom.txt1543
-rw-r--r--test/dexdump/invoke-custom.xml520
-rwxr-xr-xtest/etc/run-test-jar8
-rw-r--r--test/knownfailures.json42
-rwxr-xr-xtest/run-test8
-rwxr-xr-xtest/testrunner/testrunner.py6
-rw-r--r--test/ti-agent/breakpoint_helper.cc204
-rw-r--r--test/ti-agent/common_helper.cc812
-rw-r--r--test/ti-agent/common_helper.h16
-rw-r--r--test/ti-agent/common_load.cc13
-rw-r--r--test/ti-agent/redefinition_helper.cc410
-rw-r--r--test/ti-agent/trace_helper.cc493
-rw-r--r--test/ti-stress/stress.cc27
-rwxr-xr-xtools/buildbot-build.sh2
-rw-r--r--tools/dexfuzz/README3
-rw-r--r--tools/dexfuzz/src/dexfuzz/DexFuzz.java2
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/Program.java6
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java158
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java178
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java72
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java70
-rw-r--r--tools/libcore_gcstress_failures.txt13
-rwxr-xr-xtools/run-libcore-tests.sh9
280 files changed, 16756 insertions, 2882 deletions
diff --git a/Android.mk b/Android.mk
index 7de07a89a0..7eb0bf926e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -81,7 +81,6 @@ include $(art_path)/tools/Android.mk
include $(art_path)/tools/ahat/Android.mk
include $(art_path)/tools/dexfuzz/Android.mk
include $(art_path)/libart_fake/Android.mk
-include $(art_path)/test/Android.run-test-jvmti-java-library.mk
ART_HOST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
@@ -375,6 +374,26 @@ LOCAL_REQUIRED_MODULES := \
# For nosy apps, we provide a fake library that avoids namespace issues and gives some warnings.
LOCAL_REQUIRED_MODULES += libart_fake
+# Potentially add in debug variants:
+#
+# * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
+# * We will always add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = true.
+# * Otherwise, we will add them by default to userdebug and eng builds.
+art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
+ifneq (false,$(art_target_include_debug_build))
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+ art_target_include_debug_build := true
+endif
+ifeq (true,$(art_target_include_debug_build))
+LOCAL_REQUIRED_MODULES += \
+ libartd \
+ libartd-compiler \
+ libopenjdkjvmd \
+ libopenjdkjvmtid \
+
+endif
+endif
+
include $(BUILD_PHONY_PACKAGE)
# The art-tools package depends on helpers and tools that are useful for developers and on-device
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000000..7297a14da2
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+ngeoffray@google.com
+sehr@google.com
+*
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c87abe5664..b6ffcc54f7 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -30,6 +30,10 @@ GTEST_DEX_DIRECTORIES := \
ErroneousA \
ErroneousB \
ErroneousInit \
+ ForClassLoaderA \
+ ForClassLoaderB \
+ ForClassLoaderC \
+ ForClassLoaderD \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -99,7 +103,7 @@ $(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMul
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/build/art.go b/build/art.go
index f52c63525a..1b9c646433 100644
--- a/build/art.go
+++ b/build/art.go
@@ -19,8 +19,6 @@ import (
"android/soong/cc"
"fmt"
"sync"
-
- "github.com/google/blueprint"
)
var supportedArches = []string{"arm", "arm64", "mips", "mips64", "x86", "x86_64"}
@@ -83,20 +81,20 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) {
// the debug version. So make the gap consistent (and adjust for the worst).
if len(ctx.AConfig().SanitizeDevice()) > 0 || len(ctx.AConfig().SanitizeHost()) > 0 {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
- "-DART_STACK_OVERFLOW_GAP_arm64=8192",
- "-DART_STACK_OVERFLOW_GAP_mips=16384",
- "-DART_STACK_OVERFLOW_GAP_mips64=16384",
- "-DART_STACK_OVERFLOW_GAP_x86=16384",
- "-DART_STACK_OVERFLOW_GAP_x86_64=20480")
+ "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_mips=16384",
+ "-DART_STACK_OVERFLOW_GAP_mips64=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86_64=20480")
} else {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
- "-DART_STACK_OVERFLOW_GAP_arm64=8192",
- "-DART_STACK_OVERFLOW_GAP_mips=16384",
- "-DART_STACK_OVERFLOW_GAP_mips64=16384",
- "-DART_STACK_OVERFLOW_GAP_x86=8192",
- "-DART_STACK_OVERFLOW_GAP_x86_64=8192")
+ "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_mips=16384",
+ "-DART_STACK_OVERFLOW_GAP_mips64=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86=8192",
+ "-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
return cflags, asflags
@@ -168,10 +166,10 @@ func globalDefaults(ctx android.LoadHookContext) {
Cflags []string
}
}
- Cflags []string
- Asflags []string
+ Cflags []string
+ Asflags []string
Sanitize struct {
- Recover []string
+ Recover []string
}
}
@@ -182,7 +180,7 @@ func globalDefaults(ctx android.LoadHookContext) {
if envTrue(ctx, "ART_DEX_FILE_ACCESS_TRACKING") {
p.Cflags = append(p.Cflags, "-DART_DEX_FILE_ACCESS_TRACKING")
- p.Sanitize.Recover = []string {
+ p.Sanitize.Recover = []string{
"address",
}
}
@@ -266,67 +264,67 @@ func init() {
android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
}
-func artGlobalDefaultsFactory() (blueprint.Module, []interface{}) {
- module, props := artDefaultsFactory()
+func artGlobalDefaultsFactory() android.Module {
+ module := artDefaultsFactory()
android.AddLoadHook(module, globalDefaults)
- return module, props
+ return module
}
-func artDebugDefaultsFactory() (blueprint.Module, []interface{}) {
- module, props := artDefaultsFactory()
+func artDebugDefaultsFactory() android.Module {
+ module := artDefaultsFactory()
android.AddLoadHook(module, debugDefaults)
- return module, props
+ return module
}
-func artDefaultsFactory() (blueprint.Module, []interface{}) {
+func artDefaultsFactory() android.Module {
c := &codegenProperties{}
- module, props := cc.DefaultsFactory(c)
+ module := cc.DefaultsFactory(c)
android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, true) })
- return module, props
+ return module
}
-func artLibrary() (blueprint.Module, []interface{}) {
+func artLibrary() android.Module {
library, _ := cc.NewLibrary(android.HostAndDeviceSupported)
- module, props := library.Init()
+ module := library.Init()
- props = installCodegenCustomizer(module, props, true)
+ installCodegenCustomizer(module, true)
- return module, props
+ return module
}
-func artBinary() (blueprint.Module, []interface{}) {
+func artBinary() android.Module {
binary, _ := cc.NewBinary(android.HostAndDeviceSupported)
- module, props := binary.Init()
+ module := binary.Init()
android.AddLoadHook(module, customLinker)
android.AddLoadHook(module, prefer32Bit)
- return module, props
+ return module
}
-func artTest() (blueprint.Module, []interface{}) {
+func artTest() android.Module {
test := cc.NewTest(android.HostAndDeviceSupported)
- module, props := test.Init()
+ module := test.Init()
- props = installCodegenCustomizer(module, props, false)
+ installCodegenCustomizer(module, false)
android.AddLoadHook(module, customLinker)
android.AddLoadHook(module, prefer32Bit)
android.AddInstallHook(module, testInstall)
- return module, props
+ return module
}
-func artTestLibrary() (blueprint.Module, []interface{}) {
+func artTestLibrary() android.Module {
test := cc.NewTestLibrary(android.HostAndDeviceSupported)
- module, props := test.Init()
+ module := test.Init()
- props = installCodegenCustomizer(module, props, false)
+ installCodegenCustomizer(module, false)
android.AddLoadHook(module, prefer32Bit)
android.AddInstallHook(module, testInstall)
- return module, props
+ return module
}
func envDefault(ctx android.BaseContext, key string, defaultValue string) string {
diff --git a/build/codegen.go b/build/codegen.go
index ba6f2142c9..8526bf192b 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -22,8 +22,6 @@ import (
"android/soong/android"
"sort"
"strings"
-
- "github.com/google/blueprint"
)
func codegen(ctx android.LoadHookContext, c *codegenProperties, library bool) {
@@ -159,10 +157,8 @@ func defaultDeviceCodegenArches(ctx android.LoadHookContext) []string {
return ret
}
-func installCodegenCustomizer(module blueprint.Module, props []interface{}, library bool) []interface{} {
+func installCodegenCustomizer(module android.Module, library bool) {
c := &codegenProperties{}
android.AddLoadHook(module, func(ctx android.LoadHookContext) { codegen(ctx, c, library) })
- props = append(props, c)
-
- return props
+ module.AddProperties(c)
}
diff --git a/compiler/Android.bp b/compiler/Android.bp
index a1269dcaf9..761f53fa5a 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -356,6 +356,7 @@ art_cc_test {
"optimizing/live_interval_test.cc",
"optimizing/loop_optimization_test.cc",
"optimizing/nodes_test.cc",
+ "optimizing/nodes_vector_test.cc",
"optimizing/parallel_move_test.cc",
"optimizing/pretty_printer_test.cc",
"optimizing/reference_type_propagation_test.cc",
@@ -429,13 +430,20 @@ art_cc_test {
shared_libs: [
"libartd-compiler",
- "libartd-simulator",
"libvixld-arm",
"libvixld-arm64",
"libbacktrace",
"libnativeloader",
],
+
+ target: {
+ host: {
+ shared_libs: [
+ "libartd-simulator",
+ ],
+ },
+ },
}
art_cc_test {
@@ -455,6 +463,7 @@ art_cc_test {
srcs: [
"optimizing/emit_swap_mips_test.cc",
"utils/mips/assembler_mips_test.cc",
+ "utils/mips/assembler_mips32r5_test.cc",
"utils/mips/assembler_mips32r6_test.cc",
],
},
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 8cc1cc38e2..db95bd6e03 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -102,46 +102,17 @@ inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField(
return std::make_pair(fast_get, fast_put);
}
-template <typename ArtMember>
-inline bool CompilerDriver::CanAccessResolvedMember(mirror::Class* referrer_class ATTRIBUTE_UNUSED,
- mirror::Class* access_to ATTRIBUTE_UNUSED,
- ArtMember* member ATTRIBUTE_UNUSED,
- mirror::DexCache* dex_cache ATTRIBUTE_UNUSED,
- uint32_t field_idx ATTRIBUTE_UNUSED) {
- // Not defined for ArtMember values other than ArtField or ArtMethod.
- UNREACHABLE();
-}
-
-template <>
-inline bool CompilerDriver::CanAccessResolvedMember<ArtField>(mirror::Class* referrer_class,
- mirror::Class* access_to,
- ArtField* field,
- mirror::DexCache* dex_cache,
- uint32_t field_idx) {
- return referrer_class->CanAccessResolvedField(access_to, field, dex_cache, field_idx);
-}
-
-template <>
-inline bool CompilerDriver::CanAccessResolvedMember<ArtMethod>(
- mirror::Class* referrer_class,
- mirror::Class* access_to,
- ArtMethod* method,
- mirror::DexCache* dex_cache,
- uint32_t field_idx) {
- return referrer_class->CanAccessResolvedMethod(access_to, method, dex_cache, field_idx);
-}
-
inline ArtMethod* CompilerDriver::ResolveMethod(
- ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
- uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
+ ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexCompilationUnit* mUnit,
+ uint32_t method_idx,
+ InvokeType invoke_type) {
DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
ArtMethod* resolved_method =
- check_incompatible_class_change
- ? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
- *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type)
- : mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
- *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
+ mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
+ *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
if (UNLIKELY(resolved_method == nullptr)) {
DCHECK(soa.Self()->IsExceptionPending());
// Clean up any exception left by type resolution.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 622448fc59..bb64755c9e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -87,6 +87,10 @@ static constexpr bool kTimeCompileMethod = !kIsDebugBuild;
// Print additional info during profile guided compilation.
static constexpr bool kDebugProfileGuidedCompilation = false;
+// Max encoded fields allowed for initializing app image. Hardcode the number for now
+// because 5000 should be large enough.
+static constexpr uint32_t kMaxEncodedFields = 5000;
+
static double Percentage(size_t x, size_t y) {
return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y));
}
@@ -2244,6 +2248,10 @@ class InitializeClassVisitor : public CompilationVisitor {
mirror::Class::Status old_status = klass->GetStatus();;
// Only try to initialize classes that were successfully verified.
if (klass->IsVerified()) {
+ // Don't initialize classes in boot space when compiling app image
+ if (is_app_image && klass->IsBootStrapClassLoaded()) {
+ return;
+ }
// Attempt to initialize the class but bail if we either need to initialize the super-class
// or static fields.
manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false);
@@ -2261,12 +2269,25 @@ class InitializeClassVisitor : public CompilationVisitor {
ObjectLock<mirror::Class> lock(soa.Self(), h_klass);
// Attempt to initialize allowing initialization of parent classes but still not static
// fields.
- manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
+ // Initialize dependencies first only for app image, to make TryInitialize recursive.
+ bool is_superclass_initialized = !is_app_image ? true :
+ InitializeDependencies(klass, class_loader, soa.Self());
+ if (!is_app_image || (is_app_image && is_superclass_initialized)) {
+ manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
+ }
+ // Otherwise it's in app image but superclasses can't be initialized, no need to proceed.
old_status = klass->GetStatus();
+
+ bool too_many_encoded_fields = false;
+ if (!is_boot_image && klass->NumStaticFields() > kMaxEncodedFields) {
+ too_many_encoded_fields = true;
+ }
// If the class was not initialized, we can proceed to see if we can initialize static
- // fields.
+ // fields. Limit the max number of encoded fields.
if (!klass->IsInitialized() &&
(is_app_image || is_boot_image) &&
+ is_superclass_initialized &&
+ !too_many_encoded_fields &&
manager_->GetCompiler()->IsImageClass(descriptor)) {
bool can_init_static_fields = false;
if (is_boot_image) {
@@ -2278,8 +2299,6 @@ class InitializeClassVisitor : public CompilationVisitor {
CHECK(is_app_image);
// The boot image case doesn't need to recursively initialize the dependencies with
// special logic since the class linker already does this.
- bool is_superclass_initialized =
- InitializeDependencies(klass, class_loader, soa.Self());
can_init_static_fields =
!soa.Self()->IsExceptionPending() &&
is_superclass_initialized &&
@@ -2406,30 +2425,6 @@ class InitializeClassVisitor : public CompilationVisitor {
}
}
- bool NoPotentialInternStrings(Handle<mirror::Class> klass,
- Handle<mirror::ClassLoader>* class_loader)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache());
- const DexFile* dex_file = h_dex_cache->GetDexFile();
- const DexFile::ClassDef* class_def = klass->GetClassDef();
- annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file,
- &h_dex_cache,
- class_loader,
- manager_->GetClassLinker(),
- *class_def);
-
- const auto jString = annotations::RuntimeEncodedStaticFieldValueIterator::kString;
- for ( ; value_it.HasNext(); value_it.Next()) {
- if (value_it.GetValueType() == jString) {
- // We don't want cache the static encoded strings which is a potential intern.
- return false;
- }
- }
-
- return true;
- }
-
bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally.
@@ -2559,7 +2554,7 @@ class InitializeClassVisitor : public CompilationVisitor {
}
}
- return NoPotentialInternStrings(klass, class_loader);
+ return true;
}
const ParallelCompilationManager* const manager_;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 69f7b1bd3c..e9e73787e4 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -254,9 +254,12 @@ class CompilerDriver {
// Resolve a method. Returns null on failure, including incompatible class change.
ArtMethod* ResolveMethod(
- ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
- uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true)
+ ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexCompilationUnit* mUnit,
+ uint32_t method_idx,
+ InvokeType invoke_type)
REQUIRES_SHARED(Locks::mutator_lock_);
void ProcessedInstanceField(bool resolved);
@@ -381,19 +384,6 @@ class CompilerDriver {
}
private:
- // Can `referrer_class` access the resolved `member`?
- // Dispatch call to mirror::Class::CanAccessResolvedField or
- // mirror::Class::CanAccessResolvedMember depending on the value of
- // ArtMember.
- template <typename ArtMember>
- static bool CanAccessResolvedMember(mirror::Class* referrer_class,
- mirror::Class* access_to,
- ArtMember* member,
- mirror::DexCache* dex_cache,
- uint32_t field_idx)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index a4e2083fe4..76f0ae9202 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -40,7 +40,7 @@ CompilerOptions::CompilerOptions()
implicit_so_checks_(true),
implicit_suspend_checks_(false),
compile_pic_(false),
- verbose_methods_(nullptr),
+ verbose_methods_(),
abort_on_hard_verifier_failure_(false),
init_failure_output_(nullptr),
dump_cfg_file_name_(""),
@@ -55,58 +55,6 @@ CompilerOptions::~CompilerOptions() {
// because we don't want to include the PassManagerOptions definition from the header file.
}
-CompilerOptions::CompilerOptions(CompilerFilter::Filter compiler_filter,
- size_t huge_method_threshold,
- size_t large_method_threshold,
- size_t small_method_threshold,
- size_t tiny_method_threshold,
- size_t num_dex_methods_threshold,
- size_t inline_max_code_units,
- const std::vector<const DexFile*>* no_inline_from,
- double top_k_profile_threshold,
- bool debuggable,
- bool generate_debug_info,
- bool implicit_null_checks,
- bool implicit_so_checks,
- bool implicit_suspend_checks,
- bool compile_pic,
- const std::vector<std::string>* verbose_methods,
- std::ostream* init_failure_output,
- bool abort_on_hard_verifier_failure,
- const std::string& dump_cfg_file_name,
- bool dump_cfg_append,
- bool force_determinism,
- RegisterAllocator::Strategy regalloc_strategy,
- const std::vector<std::string>* passes_to_run)
- : compiler_filter_(compiler_filter),
- huge_method_threshold_(huge_method_threshold),
- large_method_threshold_(large_method_threshold),
- small_method_threshold_(small_method_threshold),
- tiny_method_threshold_(tiny_method_threshold),
- num_dex_methods_threshold_(num_dex_methods_threshold),
- inline_max_code_units_(inline_max_code_units),
- no_inline_from_(no_inline_from),
- boot_image_(false),
- app_image_(false),
- top_k_profile_threshold_(top_k_profile_threshold),
- debuggable_(debuggable),
- generate_debug_info_(generate_debug_info),
- generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo),
- generate_build_id_(false),
- implicit_null_checks_(implicit_null_checks),
- implicit_so_checks_(implicit_so_checks),
- implicit_suspend_checks_(implicit_suspend_checks),
- compile_pic_(compile_pic),
- verbose_methods_(verbose_methods),
- abort_on_hard_verifier_failure_(abort_on_hard_verifier_failure),
- init_failure_output_(init_failure_output),
- dump_cfg_file_name_(dump_cfg_file_name),
- dump_cfg_append_(dump_cfg_append),
- force_determinism_(force_determinism),
- register_allocation_strategy_(regalloc_strategy),
- passes_to_run_(passes_to_run) {
-}
-
void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
}
@@ -204,6 +152,11 @@ bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usa
dump_cfg_append_ = true;
} else if (option.starts_with("--register-allocation-strategy=")) {
ParseRegisterAllocationStrategy(option, Usage);
+ } else if (option.starts_with("--verbose-methods=")) {
+ // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
+ // conditional on having verbose methods.
+ gLogVerbosity.compiler = false;
+ Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
} else {
// Option not recognized.
return false;
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 89c2537476..b99263db0e 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -52,30 +52,6 @@ class CompilerOptions FINAL {
CompilerOptions();
~CompilerOptions();
- CompilerOptions(CompilerFilter::Filter compiler_filter,
- size_t huge_method_threshold,
- size_t large_method_threshold,
- size_t small_method_threshold,
- size_t tiny_method_threshold,
- size_t num_dex_methods_threshold,
- size_t inline_max_code_units,
- const std::vector<const DexFile*>* no_inline_from,
- double top_k_profile_threshold,
- bool debuggable,
- bool generate_debug_info,
- bool implicit_null_checks,
- bool implicit_so_checks,
- bool implicit_suspend_checks,
- bool compile_pic,
- const std::vector<std::string>* verbose_methods,
- std::ostream* init_failure_output,
- bool abort_on_hard_verifier_failure,
- const std::string& dump_cfg_file_name,
- bool dump_cfg_append,
- bool force_determinism,
- RegisterAllocator::Strategy regalloc_strategy,
- const std::vector<std::string>* passes_to_run);
-
CompilerFilter::Filter GetCompilerFilter() const {
return compiler_filter_;
}
@@ -163,6 +139,10 @@ class CompilerOptions FINAL {
return debuggable_;
}
+ void SetDebuggable(bool value) {
+ debuggable_ = value;
+ }
+
bool GetNativeDebuggable() const {
return GetDebuggable() && GetGenerateDebugInfo();
}
@@ -211,11 +191,11 @@ class CompilerOptions FINAL {
}
bool HasVerboseMethods() const {
- return verbose_methods_ != nullptr && !verbose_methods_->empty();
+ return !verbose_methods_.empty();
}
bool IsVerboseMethod(const std::string& pretty_method) const {
- for (const std::string& cur_method : *verbose_methods_) {
+ for (const std::string& cur_method : verbose_methods_) {
if (pretty_method.find(cur_method) != std::string::npos) {
return true;
}
@@ -299,7 +279,7 @@ class CompilerOptions FINAL {
bool compile_pic_;
// Vector of methods to have verbose output enabled for.
- const std::vector<std::string>* verbose_methods_;
+ std::vector<std::string> verbose_methods_;
// Abort compilation with an error if we find a class that fails verification with a hard
// failure.
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index a8fdecaa4a..1c73dfab37 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -398,12 +398,18 @@ void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) {
// Before we stomp over the lock word, save the hash code for later.
LockWord lw(object->GetLockWord(false));
switch (lw.GetState()) {
- case LockWord::kFatLocked: {
- LOG(FATAL) << "Fat locked object " << object << " found during object copy";
- break;
- }
+ case LockWord::kFatLocked:
+ FALLTHROUGH_INTENDED;
case LockWord::kThinLocked: {
- LOG(FATAL) << "Thin locked object " << object << " found during object copy";
+ std::ostringstream oss;
+ bool thin = (lw.GetState() == LockWord::kThinLocked);
+ oss << (thin ? "Thin" : "Fat")
+ << " locked object " << object << "(" << object->PrettyTypeOf()
+ << ") found during object copy";
+ if (thin) {
+ oss << ". Lock owner:" << lw.ThinLockOwner();
+ }
+ LOG(FATAL) << oss.str();
break;
}
case LockWord::kUnlocked:
@@ -473,6 +479,11 @@ void ImageWriter::PrepareDexCacheArraySlots() {
start + layout.MethodTypesOffset(),
dex_cache);
}
+ if (dex_cache->GetResolvedCallSites() != nullptr) {
+ AddDexCacheArrayRelocation(dex_cache->GetResolvedCallSites(),
+ start + layout.CallSitesOffset(),
+ dex_cache);
+ }
}
}
@@ -946,11 +957,18 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
ArtMethod* method =
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();
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a MethodId
+ // results in resolving the referenced class and that can for example throw OOME.
+ ObjPtr<mirror::Class> referencing_class = class_linker->LookupResolvedType(
+ dex_file,
+ dex_file.GetMethodId(i).class_idx_,
+ dex_cache,
+ class_loader);
// 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->IsCopied() || !KeepClass(declaring_class)) {
+ if (method->IsCopied() || !KeepClass(referencing_class)) {
mirror::DexCache::SetElementPtrSize(resolved_methods,
i,
resolution_method,
@@ -958,8 +976,8 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
} else if (kIsDebugBuild) {
// Check that the class is still in the classes table.
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- CHECK(class_linker->ClassInClassTable(declaring_class)) << "Class "
- << Class::PrettyClass(declaring_class) << " not in class linker table";
+ CHECK(class_linker->ClassInClassTable(referencing_class)) << "Class "
+ << Class::PrettyClass(referencing_class) << " not in class linker table";
}
}
// Prune fields and make the contents of the field array deterministic.
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 66135414f7..28a3f1edae 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -90,36 +90,20 @@ NO_RETURN static void Usage(const char* fmt, ...) {
}
JitCompiler::JitCompiler() {
- compiler_options_.reset(new CompilerOptions(
- CompilerFilter::kDefaultCompilerFilter,
- CompilerOptions::kDefaultHugeMethodThreshold,
- CompilerOptions::kDefaultLargeMethodThreshold,
- CompilerOptions::kDefaultSmallMethodThreshold,
- CompilerOptions::kDefaultTinyMethodThreshold,
- CompilerOptions::kDefaultNumDexMethodsThreshold,
- CompilerOptions::kDefaultInlineMaxCodeUnits,
- /* no_inline_from */ nullptr,
- CompilerOptions::kDefaultTopKProfileThreshold,
- Runtime::Current()->IsJavaDebuggable(),
- CompilerOptions::kDefaultGenerateDebugInfo,
- /* implicit_null_checks */ true,
- /* implicit_so_checks */ true,
- /* implicit_suspend_checks */ false,
- /* pic */ false,
- /* verbose_methods */ nullptr,
- /* init_failure_output */ nullptr,
- /* abort_on_hard_verifier_failure */ false,
- /* dump_cfg_file_name */ "",
- /* dump_cfg_append */ false,
- /* force_determinism */ false,
- RegisterAllocator::kRegisterAllocatorDefault,
- /* passes_to_run */ nullptr));
+ compiler_options_.reset(new CompilerOptions());
for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
compiler_options_->ParseCompilerOption(argument, Usage);
}
// JIT is never PIC, no matter what the runtime compiler options specify.
compiler_options_->SetNonPic();
+ // Set debuggability based on the runtime value.
+ compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable());
+
+ // Special case max code units for inlining, whose default is "unset" (implictly
+ // meaning no limit).
+ compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+
const InstructionSet instruction_set = kRuntimeISA;
for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) {
VLOG(compiler) << "JIT compiler option " << option;
diff --git a/compiler/linker/mips/relative_patcher_mips.cc b/compiler/linker/mips/relative_patcher_mips.cc
index d99d237a23..3bec30f1e8 100644
--- a/compiler/linker/mips/relative_patcher_mips.cc
+++ b/compiler/linker/mips/relative_patcher_mips.cc
@@ -49,43 +49,27 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
uint32_t target_offset) {
uint32_t anchor_literal_offset = patch.PcInsnOffset();
uint32_t literal_offset = patch.LiteralOffset();
- uint32_t literal_low_offset;
+ bool high_patch = ((*code)[literal_offset + 0] == 0x34) && ((*code)[literal_offset + 1] == 0x12);
- // Perform basic sanity checks and initialize `literal_low_offset` to point
- // to the instruction containing the 16 least significant bits of the
- // relative address.
- if (is_r6) {
- DCHECK_GE(code->size(), 8u);
- DCHECK_LE(literal_offset, code->size() - 8u);
- DCHECK_EQ(literal_offset, anchor_literal_offset);
- // AUIPC reg, offset_high
- DCHECK_EQ((*code)[literal_offset + 0], 0x34);
- DCHECK_EQ((*code)[literal_offset + 1], 0x12);
- DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
- DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
- // instr reg(s), offset_low
- DCHECK_EQ((*code)[literal_offset + 4], 0x78);
- DCHECK_EQ((*code)[literal_offset + 5], 0x56);
- literal_low_offset = literal_offset + 4;
+ // Perform basic sanity checks.
+ if (high_patch) {
+ if (is_r6) {
+ // auipc reg, offset_high
+ DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+ DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+ } else {
+ // lui reg, offset_high
+ DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00);
+ DCHECK_EQ((*code)[literal_offset + 3], 0x3C);
+ // addu reg, reg, reg2
+ DCHECK_EQ((*code)[literal_offset + 4], 0x21);
+ DCHECK_EQ(((*code)[literal_offset + 5] & 0x07), 0x00);
+ DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x00);
+ }
} else {
- DCHECK_GE(code->size(), 16u);
- DCHECK_LE(literal_offset, code->size() - 12u);
- DCHECK_GE(literal_offset, 4u);
- // The NAL instruction does not precede immediately as the PC+0
- // comes from HMipsComputeBaseMethodAddress.
- // LUI reg, offset_high
- DCHECK_EQ((*code)[literal_offset + 0], 0x34);
- DCHECK_EQ((*code)[literal_offset + 1], 0x12);
- DCHECK_EQ(((*code)[literal_offset + 2] & 0xE0), 0x00);
- DCHECK_EQ((*code)[literal_offset + 3], 0x3C);
- // ADDU reg, reg, reg2
- DCHECK_EQ((*code)[literal_offset + 4], 0x21);
- DCHECK_EQ(((*code)[literal_offset + 5] & 0x07), 0x00);
- DCHECK_EQ(((*code)[literal_offset + 7] & 0xFC), 0x00);
// instr reg(s), offset_low
- DCHECK_EQ((*code)[literal_offset + 8], 0x78);
- DCHECK_EQ((*code)[literal_offset + 9], 0x56);
- literal_low_offset = literal_offset + 8;
+ CHECK_EQ((*code)[literal_offset + 0], 0x78);
+ CHECK_EQ((*code)[literal_offset + 1], 0x56);
}
// Apply patch.
@@ -93,12 +77,15 @@ void MipsRelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
uint32_t diff = target_offset - anchor_offset;
diff += (diff & 0x8000) << 1; // Account for sign extension in "instr reg(s), offset_low".
- // LUI reg, offset_high / AUIPC reg, offset_high
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
- // instr reg(s), offset_low
- (*code)[literal_low_offset + 0] = static_cast<uint8_t>(diff >> 0);
- (*code)[literal_low_offset + 1] = static_cast<uint8_t>(diff >> 8);
+ if (high_patch) {
+ // lui reg, offset_high / auipc reg, offset_high
+ (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+ (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+ } else {
+ // instr reg(s), offset_low
+ (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 0);
+ (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 8);
+ }
}
void MipsRelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
diff --git a/compiler/linker/mips/relative_patcher_mips32r6_test.cc b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
index 63ad8a58d5..d1a75e28a2 100644
--- a/compiler/linker/mips/relative_patcher_mips32r6_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips32r6_test.cc
@@ -26,7 +26,9 @@ class Mips32r6RelativePatcherTest : public RelativePatcherTest {
protected:
static const uint8_t kUnpatchedPcRelativeRawCode[];
- static const uint32_t kLiteralOffset;
+ static const uint32_t kLiteralOffsetHigh;
+ static const uint32_t kLiteralOffsetLow1;
+ static const uint32_t kLiteralOffsetLow2;
static const uint32_t kAnchorOffset;
static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
@@ -44,9 +46,11 @@ class Mips32r6RelativePatcherTest : public RelativePatcherTest {
const uint8_t Mips32r6RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678
+ 0x78, 0x56, 0x52, 0x8E, // lw s2, (low(diff))(s2) ; placeholder = 0x5678
};
-const uint32_t Mips32r6RelativePatcherTest::kLiteralOffset = 0; // At auipc (where
- // patching starts).
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetHigh = 0; // At auipc.
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetLow1 = 4; // At addiu.
+const uint32_t Mips32r6RelativePatcherTest::kLiteralOffsetLow2 = 8; // At lw.
const uint32_t Mips32r6RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points).
const ArrayRef<const uint8_t> Mips32r6RelativePatcherTest::kUnpatchedPcRelativeCode(
kUnpatchedPcRelativeRawCode);
@@ -60,11 +64,12 @@ void Mips32r6RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const Link
ASSERT_TRUE(result.first);
uint32_t diff = target_offset - (result.second + kAnchorOffset);
- diff += (diff & 0x8000) << 1; // Account for sign extension in addiu.
+ diff += (diff & 0x8000) << 1; // Account for sign extension in addiu/lw.
const uint8_t expected_code[] = {
static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
+ static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x8E,
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
@@ -75,7 +80,9 @@ void Mips32r6RelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
bss_begin_ = bss_begin;
LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
}
@@ -84,7 +91,9 @@ void Mips32r6RelativePatcherTest::TestStringReference(uint32_t string_offset) {
constexpr uint32_t kStringIndex = 1u;
string_index_to_offset_map_.Put(kStringIndex, string_offset);
LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+ LinkerPatch::RelativeStringPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::RelativeStringPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::RelativeStringPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
}
diff --git a/compiler/linker/mips/relative_patcher_mips_test.cc b/compiler/linker/mips/relative_patcher_mips_test.cc
index 49af7c614b..2f7a0752a6 100644
--- a/compiler/linker/mips/relative_patcher_mips_test.cc
+++ b/compiler/linker/mips/relative_patcher_mips_test.cc
@@ -26,7 +26,9 @@ class MipsRelativePatcherTest : public RelativePatcherTest {
protected:
static const uint8_t kUnpatchedPcRelativeRawCode[];
- static const uint32_t kLiteralOffset;
+ static const uint32_t kLiteralOffsetHigh;
+ static const uint32_t kLiteralOffsetLow1;
+ static const uint32_t kLiteralOffsetLow2;
static const uint32_t kAnchorOffset;
static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
@@ -46,8 +48,11 @@ const uint8_t MipsRelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
0x34, 0x12, 0x12, 0x3C, // lui s2, high(diff); placeholder = 0x1234
0x21, 0x90, 0x5F, 0x02, // addu s2, s2, ra
0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678
+ 0x78, 0x56, 0x52, 0x8E, // lw s2, (low(diff))(s2) ; placeholder = 0x5678
};
-const uint32_t MipsRelativePatcherTest::kLiteralOffset = 4; // At lui (where patching starts).
+const uint32_t MipsRelativePatcherTest::kLiteralOffsetHigh = 4; // At lui.
+const uint32_t MipsRelativePatcherTest::kLiteralOffsetLow1 = 12; // At addiu.
+const uint32_t MipsRelativePatcherTest::kLiteralOffsetLow2 = 16; // At lw.
const uint32_t MipsRelativePatcherTest::kAnchorOffset = 8; // At addu (where PC+0 points).
const ArrayRef<const uint8_t> MipsRelativePatcherTest::kUnpatchedPcRelativeCode(
kUnpatchedPcRelativeRawCode);
@@ -61,13 +66,14 @@ void MipsRelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPa
ASSERT_TRUE(result.first);
uint32_t diff = target_offset - (result.second + kAnchorOffset);
- diff += (diff & 0x8000) << 1; // Account for sign extension in addiu.
+ diff += (diff & 0x8000) << 1; // Account for sign extension in addiu/lw.
const uint8_t expected_code[] = {
0x00, 0x00, 0x10, 0x04,
static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x12, 0x3C,
0x21, 0x90, 0x5F, 0x02,
static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
+ static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x8E,
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
@@ -78,7 +84,9 @@ void MipsRelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
bss_begin_ = bss_begin;
LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
}
@@ -87,7 +95,9 @@ void MipsRelativePatcherTest::TestStringReference(uint32_t string_offset) {
constexpr uint32_t kStringIndex = 1u;
string_index_to_offset_map_.Put(kStringIndex, string_offset);
LinkerPatch patches[] = {
- LinkerPatch::RelativeStringPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+ LinkerPatch::RelativeStringPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::RelativeStringPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::RelativeStringPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), string_offset);
}
diff --git a/compiler/linker/mips64/relative_patcher_mips64.cc b/compiler/linker/mips64/relative_patcher_mips64.cc
index 3488d6d21c..d9f4758eb1 100644
--- a/compiler/linker/mips64/relative_patcher_mips64.cc
+++ b/compiler/linker/mips64/relative_patcher_mips64.cc
@@ -36,38 +36,11 @@ uint32_t Mips64RelativePatcher::WriteThunks(OutputStream* out ATTRIBUTE_UNUSED,
return offset; // No thunks added; no limit on relative call distance.
}
-void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code,
- uint32_t literal_offset,
- uint32_t patch_offset,
- uint32_t target_offset) {
- // Basic sanity checks.
- DCHECK_GE(code->size(), 8u);
- DCHECK_LE(literal_offset, code->size() - 8u);
- // auipc reg, offset_high
- DCHECK_EQ((*code)[literal_offset + 0], 0x34);
- DCHECK_EQ((*code)[literal_offset + 1], 0x12);
- DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
- DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
- // jialc reg, offset_low
- DCHECK_EQ((*code)[literal_offset + 4], 0x78);
- DCHECK_EQ((*code)[literal_offset + 5], 0x56);
- DCHECK_EQ(((*code)[literal_offset + 6] & 0xE0), 0x00);
- DCHECK_EQ((*code)[literal_offset + 7], 0xF8);
-
- // Apply patch.
- uint32_t diff = target_offset - patch_offset;
- // Note that a combination of auipc with an instruction that adds a sign-extended
- // 16-bit immediate operand (e.g. jialc) provides a PC-relative range of
- // PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
- // by 32KB.
- diff += (diff & 0x8000) << 1; // Account for sign extension in jialc.
-
- // auipc reg, offset_high
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
- // jialc reg, offset_low
- (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
- (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
+void Mips64RelativePatcher::PatchCall(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
+ uint32_t literal_offset ATTRIBUTE_UNUSED,
+ uint32_t patch_offset ATTRIBUTE_UNUSED,
+ uint32_t target_offset ATTRIBUTE_UNUSED) {
+ UNIMPLEMENTED(FATAL) << "PatchCall unimplemented on MIPS64";
}
void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
@@ -76,19 +49,18 @@ void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
uint32_t target_offset) {
uint32_t anchor_literal_offset = patch.PcInsnOffset();
uint32_t literal_offset = patch.LiteralOffset();
+ bool high_patch = ((*code)[literal_offset + 0] == 0x34) && ((*code)[literal_offset + 1] == 0x12);
- // Basic sanity checks.
- DCHECK_GE(code->size(), 8u);
- DCHECK_LE(literal_offset, code->size() - 8u);
- DCHECK_EQ(literal_offset, anchor_literal_offset);
- // auipc reg, offset_high
- DCHECK_EQ((*code)[literal_offset + 0], 0x34);
- DCHECK_EQ((*code)[literal_offset + 1], 0x12);
- DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
- DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
- // instr reg(s), offset_low
- DCHECK_EQ((*code)[literal_offset + 4], 0x78);
- DCHECK_EQ((*code)[literal_offset + 5], 0x56);
+ // Perform basic sanity checks.
+ if (high_patch) {
+ // auipc reg, offset_high
+ DCHECK_EQ(((*code)[literal_offset + 2] & 0x1F), 0x1E);
+ DCHECK_EQ(((*code)[literal_offset + 3] & 0xFC), 0xEC);
+ } else {
+ // instr reg(s), offset_low
+ CHECK_EQ((*code)[literal_offset + 0], 0x78);
+ CHECK_EQ((*code)[literal_offset + 1], 0x56);
+ }
// Apply patch.
uint32_t anchor_offset = patch_offset - literal_offset + anchor_literal_offset;
@@ -97,14 +69,17 @@ void Mips64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code,
// 16-bit immediate operand (e.g. ld) provides a PC-relative range of
// PC-0x80000000 to PC+0x7FFF7FFF on MIPS64, that is, short of 2GB on one end
// by 32KB.
- diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc.
+ diff += (diff & 0x8000) << 1; // Account for sign extension in "instr reg(s), offset_low".
- // auipc reg, offset_high
- (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
- (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
- // instr reg(s), offset_low
- (*code)[literal_offset + 4] = static_cast<uint8_t>(diff >> 0);
- (*code)[literal_offset + 5] = static_cast<uint8_t>(diff >> 8);
+ if (high_patch) {
+ // auipc reg, offset_high
+ (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 16);
+ (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 24);
+ } else {
+ // instr reg(s), offset_low
+ (*code)[literal_offset + 0] = static_cast<uint8_t>(diff >> 0);
+ (*code)[literal_offset + 1] = static_cast<uint8_t>(diff >> 8);
+ }
}
void Mips64RelativePatcher::PatchBakerReadBarrierBranch(std::vector<uint8_t>* code ATTRIBUTE_UNUSED,
diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc
index 9c9e24a96e..a5f494d645 100644
--- a/compiler/linker/mips64/relative_patcher_mips64_test.cc
+++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc
@@ -27,10 +27,11 @@ class Mips64RelativePatcherTest : public RelativePatcherTest {
protected:
static const uint8_t kUnpatchedPcRelativeRawCode[];
static const uint8_t kUnpatchedPcRelativeCallRawCode[];
- static const uint32_t kLiteralOffset;
+ static const uint32_t kLiteralOffsetHigh;
+ static const uint32_t kLiteralOffsetLow1;
+ static const uint32_t kLiteralOffsetLow2;
static const uint32_t kAnchorOffset;
static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCode;
- static const ArrayRef<const uint8_t> kUnpatchedPcRelativeCallCode;
uint32_t GetMethodOffset(uint32_t method_idx) {
auto result = method_offset_map_.FindMethodOffset(MethodRef(method_idx));
@@ -44,19 +45,16 @@ class Mips64RelativePatcherTest : public RelativePatcherTest {
};
const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
- 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
+ 0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
0x78, 0x56, 0x52, 0x66, // daddiu s2, s2, low(diff); placeholder = 0x5678
+ 0x78, 0x56, 0x52, 0x9E, // lwu s2, (low(diff))(s2) ; placeholder = 0x5678
};
-const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeCallRawCode[] = {
- 0x34, 0x12, 0x3E, 0xEC, // auipc at, high(diff); placeholder = 0x1234
- 0x78, 0x56, 0x01, 0xF8, // jialc at, low(diff); placeholder = 0x5678
-};
-const uint32_t Mips64RelativePatcherTest::kLiteralOffset = 0; // At auipc (where patching starts).
+const uint32_t Mips64RelativePatcherTest::kLiteralOffsetHigh = 0; // At auipc.
+const uint32_t Mips64RelativePatcherTest::kLiteralOffsetLow1 = 4; // At daddiu.
+const uint32_t Mips64RelativePatcherTest::kLiteralOffsetLow2 = 8; // At lwu.
const uint32_t Mips64RelativePatcherTest::kAnchorOffset = 0; // At auipc (where PC+0 points).
const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCode(
kUnpatchedPcRelativeRawCode);
-const ArrayRef<const uint8_t> Mips64RelativePatcherTest::kUnpatchedPcRelativeCallCode(
- kUnpatchedPcRelativeCallRawCode);
void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const LinkerPatch>& patches,
uint32_t target_offset) {
@@ -67,11 +65,12 @@ void Mips64RelativePatcherTest::CheckPcRelativePatch(const ArrayRef<const Linker
ASSERT_TRUE(result.first);
uint32_t diff = target_offset - (result.second + kAnchorOffset);
- diff += (diff & 0x8000) << 1; // Account for sign extension in instruction following auipc.
+ diff += (diff & 0x8000) << 1; // Account for sign extension in daddiu/lwu.
const uint8_t expected_code[] = {
static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x66,
+ static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x9E,
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
@@ -82,7 +81,9 @@ void Mips64RelativePatcherTest::TestStringBssEntry(uint32_t bss_begin,
string_index_to_offset_map_.Put(kStringIndex, string_entry_offset);
bss_begin_ = bss_begin;
LinkerPatch patches[] = {
- LinkerPatch::StringBssEntryPatch(kLiteralOffset, nullptr, kAnchorOffset, kStringIndex)
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetHigh, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow1, nullptr, kAnchorOffset, kStringIndex),
+ LinkerPatch::StringBssEntryPatch(kLiteralOffsetLow2, nullptr, kAnchorOffset, kStringIndex)
};
CheckPcRelativePatch(ArrayRef<const LinkerPatch>(patches), bss_begin_ + string_entry_offset);
}
@@ -91,38 +92,5 @@ TEST_F(Mips64RelativePatcherTest, StringBssEntry) {
TestStringBssEntry(/* bss_begin */ 0x12345678, /* string_entry_offset */ 0x1234);
}
-TEST_F(Mips64RelativePatcherTest, CallOther) {
- LinkerPatch method1_patches[] = {
- LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 2u),
- };
- AddCompiledMethod(MethodRef(1u),
- kUnpatchedPcRelativeCallCode,
- ArrayRef<const LinkerPatch>(method1_patches));
- LinkerPatch method2_patches[] = {
- LinkerPatch::RelativeCodePatch(kLiteralOffset, nullptr, 1u),
- };
- AddCompiledMethod(MethodRef(2u),
- kUnpatchedPcRelativeCallCode,
- ArrayRef<const LinkerPatch>(method2_patches));
- Link();
-
- uint32_t method1_offset = GetMethodOffset(1u);
- uint32_t method2_offset = GetMethodOffset(2u);
- uint32_t diff_after = method2_offset - (method1_offset + kAnchorOffset /* PC adjustment */);
- diff_after += (diff_after & 0x8000) << 1; // Account for sign extension in jialc.
- static const uint8_t method1_expected_code[] = {
- static_cast<uint8_t>(diff_after >> 16), static_cast<uint8_t>(diff_after >> 24), 0x3E, 0xEC,
- static_cast<uint8_t>(diff_after), static_cast<uint8_t>(diff_after >> 8), 0x01, 0xF8,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(method1_expected_code)));
- uint32_t diff_before = method1_offset - (method2_offset + kAnchorOffset /* PC adjustment */);
- diff_before += (diff_before & 0x8000) << 1; // Account for sign extension in jialc.
- static const uint8_t method2_expected_code[] = {
- static_cast<uint8_t>(diff_before >> 16), static_cast<uint8_t>(diff_before >> 24), 0x3E, 0xEC,
- static_cast<uint8_t>(diff_before), static_cast<uint8_t>(diff_before >> 8), 0x01, 0xF8,
- };
- EXPECT_TRUE(CheckLinkedMethod(MethodRef(2u), ArrayRef<const uint8_t>(method2_expected_code)));
-}
-
} // namespace linker
} // namespace art
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 805a3f4366..ad3283ad4f 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -408,6 +408,17 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator {
void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
void HandleGoto(HInstruction* got, HBasicBlock* successor);
+ vixl::aarch32::MemOperand VecAddress(
+ HVecMemoryOperation* instruction,
+ // This function may acquire a scratch register.
+ vixl::aarch32::UseScratchRegisterScope* temps_scope,
+ /*out*/ vixl32::Register* scratch);
+ vixl::aarch32::AlignedMemOperand VecAddressUnaligned(
+ HVecMemoryOperation* instruction,
+ // This function may acquire a scratch register.
+ vixl::aarch32::UseScratchRegisterScope* temps_scope,
+ /*out*/ vixl32::Register* scratch);
+
ArmVIXLAssembler* const assembler_;
CodeGeneratorARMVIXL* const codegen_;
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index b39d412ac2..4c4d97bc8d 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -208,8 +208,13 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
LoadClassSlowPathMIPS(HLoadClass* cls,
HInstruction* at,
uint32_t dex_pc,
- bool do_clinit)
- : SlowPathCodeMIPS(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ bool do_clinit,
+ const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = nullptr)
+ : SlowPathCodeMIPS(at),
+ cls_(cls),
+ dex_pc_(dex_pc),
+ do_clinit_(do_clinit),
+ bss_info_high_(bss_info_high) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
@@ -217,8 +222,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
LocationSummary* locations = instruction_->GetLocations();
Location out = locations->Out();
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
- const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
- const bool r2_baker_or_no_read_barriers = !isR6 && (!kUseReadBarrier || kUseBakerReadBarrier);
+ const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier);
InvokeRuntimeCallingConvention calling_convention;
DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
const bool is_load_class_bss_entry =
@@ -228,7 +232,7 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
// For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
Register entry_address = kNoRegister;
- if (is_load_class_bss_entry && r2_baker_or_no_read_barriers) {
+ if (is_load_class_bss_entry && baker_or_no_read_barriers) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0));
// In the unlucky case that `temp` is A0, we preserve the address in `out` across the
@@ -252,9 +256,18 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
}
// For HLoadClass/kBssEntry, store the resolved class to the BSS entry.
- if (is_load_class_bss_entry && r2_baker_or_no_read_barriers) {
+ if (is_load_class_bss_entry && baker_or_no_read_barriers) {
// The class entry address was preserved in `entry_address` thanks to kSaveEverything.
- __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(0), entry_address, 0);
+ DCHECK(bss_info_high_);
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_);
+ bool reordering = __ SetReorder(false);
+ __ Bind(&info_low->label);
+ __ StoreToOffset(kStoreWord,
+ calling_convention.GetRegisterAt(0),
+ entry_address,
+ /* placeholder */ 0x5678);
+ __ SetReorder(reordering);
}
// Move the class to the desired location.
@@ -268,14 +281,17 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
RestoreLiveRegisters(codegen, locations);
// For HLoadClass/kBssEntry, store the resolved class to the BSS entry.
- if (is_load_class_bss_entry && !r2_baker_or_no_read_barriers) {
- // For non-Baker read barriers (or on R6), we need to re-calculate the address of
+ if (is_load_class_bss_entry && !baker_or_no_read_barriers) {
+ // For non-Baker read barriers we need to re-calculate the address of
// the class entry.
+ const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>();
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high);
bool reordering = __ SetReorder(false);
- mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info, TMP, base);
+ mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base, info_low);
__ StoreToOffset(kStoreWord, out.AsRegister<Register>(), TMP, /* placeholder */ 0x5678);
__ SetReorder(reordering);
}
@@ -294,12 +310,17 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS {
// Whether to initialize the class.
const bool do_clinit_;
+ // Pointer to the high half PC-relative patch info for HLoadClass/kBssEntry.
+ const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high_;
+
DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS);
};
class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
public:
- explicit LoadStringSlowPathMIPS(HLoadString* instruction) : SlowPathCodeMIPS(instruction) {}
+ explicit LoadStringSlowPathMIPS(HLoadString* instruction,
+ const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high)
+ : SlowPathCodeMIPS(instruction), bss_info_high_(bss_info_high) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
DCHECK(instruction_->IsLoadString());
@@ -310,15 +331,14 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
const dex::StringIndex string_index = load->GetStringIndex();
Register out = locations->Out().AsRegister<Register>();
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
- const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
- const bool r2_baker_or_no_read_barriers = !isR6 && (!kUseReadBarrier || kUseBakerReadBarrier);
+ const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier);
InvokeRuntimeCallingConvention calling_convention;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
// For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
Register entry_address = kNoRegister;
- if (r2_baker_or_no_read_barriers) {
+ if (baker_or_no_read_barriers) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0));
// In the unlucky case that `temp` is A0, we preserve the address in `out` across the
@@ -335,9 +355,18 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
// Store the resolved string to the BSS entry.
- if (r2_baker_or_no_read_barriers) {
+ if (baker_or_no_read_barriers) {
// The string entry address was preserved in `entry_address` thanks to kSaveEverything.
- __ StoreToOffset(kStoreWord, calling_convention.GetRegisterAt(0), entry_address, 0);
+ DCHECK(bss_info_high_);
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index, bss_info_high_);
+ bool reordering = __ SetReorder(false);
+ __ Bind(&info_low->label);
+ __ StoreToOffset(kStoreWord,
+ calling_convention.GetRegisterAt(0),
+ entry_address,
+ /* placeholder */ 0x5678);
+ __ SetReorder(reordering);
}
Primitive::Type type = instruction_->GetType();
@@ -347,14 +376,17 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
RestoreLiveRegisters(codegen, locations);
// Store the resolved string to the BSS entry.
- if (!r2_baker_or_no_read_barriers) {
- // For non-Baker read barriers (or on R6), we need to re-calculate the address of
+ if (!baker_or_no_read_barriers) {
+ // For non-Baker read barriers we need to re-calculate the address of
// the string entry.
+ const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6();
Register base = isR6 ? ZERO : locations->InAt(0).AsRegister<Register>();
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ mips_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index, info_high);
bool reordering = __ SetReorder(false);
- mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info, TMP, base);
+ mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base, info_low);
__ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678);
__ SetReorder(reordering);
}
@@ -364,6 +396,9 @@ class LoadStringSlowPathMIPS : public SlowPathCodeMIPS {
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; }
private:
+ // Pointer to the high half PC-relative patch info.
+ const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high_;
+
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS);
};
@@ -950,7 +985,9 @@ class ReadBarrierForHeapReferenceSlowPathMIPS : public SlowPathCodeMIPS {
this);
CheckEntrypointTypes<
kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
- mips_codegen->Move32(out_, calling_convention.GetReturnLocation(Primitive::kPrimNot));
+ mips_codegen->MoveLocation(out_,
+ calling_convention.GetReturnLocation(Primitive::kPrimNot),
+ Primitive::kPrimNot);
RestoreLiveRegisters(codegen, locations);
__ B(GetExitLabel());
@@ -1013,13 +1050,17 @@ class ReadBarrierForRootSlowPathMIPS : public SlowPathCodeMIPS {
InvokeRuntimeCallingConvention calling_convention;
CodeGeneratorMIPS* mips_codegen = down_cast<CodeGeneratorMIPS*>(codegen);
- mips_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), root_);
+ mips_codegen->MoveLocation(Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ root_,
+ Primitive::kPrimNot);
mips_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
instruction_,
instruction_->GetDexPc(),
this);
CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
- mips_codegen->Move32(out_, calling_convention.GetReturnLocation(Primitive::kPrimNot));
+ mips_codegen->MoveLocation(out_,
+ calling_convention.GetReturnLocation(Primitive::kPrimNot),
+ Primitive::kPrimNot);
RestoreLiveRegisters(codegen, locations);
__ B(GetExitLabel());
@@ -1407,106 +1448,92 @@ void CodeGeneratorMIPS::Bind(HBasicBlock* block) {
__ Bind(GetLabelOf(block));
}
-void CodeGeneratorMIPS::MoveLocation(Location dst, Location src, Primitive::Type dst_type) {
- if (src.Equals(dst)) {
- return;
- }
-
- if (src.IsConstant()) {
- MoveConstant(dst, src.GetConstant());
- } else {
- if (Primitive::Is64BitType(dst_type)) {
- Move64(dst, src);
- } else {
- Move32(dst, src);
- }
- }
-}
-
-void CodeGeneratorMIPS::Move32(Location destination, Location source) {
+void CodeGeneratorMIPS::MoveLocation(Location destination,
+ Location source,
+ Primitive::Type dst_type) {
if (source.Equals(destination)) {
return;
}
- if (destination.IsRegister()) {
- if (source.IsRegister()) {
- __ Move(destination.AsRegister<Register>(), source.AsRegister<Register>());
- } else if (source.IsFpuRegister()) {
- __ Mfc1(destination.AsRegister<Register>(), source.AsFpuRegister<FRegister>());
- } else {
- DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
- __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex());
- }
- } else if (destination.IsFpuRegister()) {
- if (source.IsRegister()) {
- __ Mtc1(source.AsRegister<Register>(), destination.AsFpuRegister<FRegister>());
- } else if (source.IsFpuRegister()) {
- __ MovS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
- } else {
- DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
- __ LoadSFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
- }
- } else {
- DCHECK(destination.IsStackSlot()) << destination;
- if (source.IsRegister()) {
- __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, destination.GetStackIndex());
- } else if (source.IsFpuRegister()) {
- __ StoreSToOffset(source.AsFpuRegister<FRegister>(), SP, destination.GetStackIndex());
- } else {
- DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
- __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
- __ StoreToOffset(kStoreWord, TMP, SP, destination.GetStackIndex());
- }
- }
-}
-
-void CodeGeneratorMIPS::Move64(Location destination, Location source) {
- if (source.Equals(destination)) {
- return;
- }
-
- if (destination.IsRegisterPair()) {
- if (source.IsRegisterPair()) {
- __ Move(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
- __ Move(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
- } else if (source.IsFpuRegister()) {
- Register dst_high = destination.AsRegisterPairHigh<Register>();
- Register dst_low = destination.AsRegisterPairLow<Register>();
- FRegister src = source.AsFpuRegister<FRegister>();
- __ Mfc1(dst_low, src);
- __ MoveFromFpuHigh(dst_high, src);
- } else {
- DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination;
- int32_t off = source.GetStackIndex();
- Register r = destination.AsRegisterPairLow<Register>();
- __ LoadFromOffset(kLoadDoubleword, r, SP, off);
- }
- } else if (destination.IsFpuRegister()) {
- if (source.IsRegisterPair()) {
- FRegister dst = destination.AsFpuRegister<FRegister>();
- Register src_high = source.AsRegisterPairHigh<Register>();
- Register src_low = source.AsRegisterPairLow<Register>();
- __ Mtc1(src_low, dst);
- __ MoveToFpuHigh(src_high, dst);
- } else if (source.IsFpuRegister()) {
- __ MovD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
- } else {
- DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination;
- __ LoadDFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
- }
+ if (source.IsConstant()) {
+ MoveConstant(destination, source.GetConstant());
} else {
- DCHECK(destination.IsDoubleStackSlot()) << destination;
- int32_t off = destination.GetStackIndex();
- if (source.IsRegisterPair()) {
- __ StoreToOffset(kStoreDoubleword, source.AsRegisterPairLow<Register>(), SP, off);
- } else if (source.IsFpuRegister()) {
- __ StoreDToOffset(source.AsFpuRegister<FRegister>(), SP, off);
+ if (destination.IsRegister()) {
+ if (source.IsRegister()) {
+ __ Move(destination.AsRegister<Register>(), source.AsRegister<Register>());
+ } else if (source.IsFpuRegister()) {
+ __ Mfc1(destination.AsRegister<Register>(), source.AsFpuRegister<FRegister>());
+ } else {
+ DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
+ __ LoadFromOffset(kLoadWord, destination.AsRegister<Register>(), SP, source.GetStackIndex());
+ }
+ } else if (destination.IsRegisterPair()) {
+ if (source.IsRegisterPair()) {
+ __ Move(destination.AsRegisterPairHigh<Register>(), source.AsRegisterPairHigh<Register>());
+ __ Move(destination.AsRegisterPairLow<Register>(), source.AsRegisterPairLow<Register>());
+ } else if (source.IsFpuRegister()) {
+ Register dst_high = destination.AsRegisterPairHigh<Register>();
+ Register dst_low = destination.AsRegisterPairLow<Register>();
+ FRegister src = source.AsFpuRegister<FRegister>();
+ __ Mfc1(dst_low, src);
+ __ MoveFromFpuHigh(dst_high, src);
+ } else {
+ DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination;
+ int32_t off = source.GetStackIndex();
+ Register r = destination.AsRegisterPairLow<Register>();
+ __ LoadFromOffset(kLoadDoubleword, r, SP, off);
+ }
+ } else if (destination.IsFpuRegister()) {
+ if (source.IsRegister()) {
+ DCHECK(!Primitive::Is64BitType(dst_type));
+ __ Mtc1(source.AsRegister<Register>(), destination.AsFpuRegister<FRegister>());
+ } else if (source.IsRegisterPair()) {
+ DCHECK(Primitive::Is64BitType(dst_type));
+ FRegister dst = destination.AsFpuRegister<FRegister>();
+ Register src_high = source.AsRegisterPairHigh<Register>();
+ Register src_low = source.AsRegisterPairLow<Register>();
+ __ Mtc1(src_low, dst);
+ __ MoveToFpuHigh(src_high, dst);
+ } else if (source.IsFpuRegister()) {
+ if (Primitive::Is64BitType(dst_type)) {
+ __ MovD(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
+ } else {
+ DCHECK_EQ(dst_type, Primitive::kPrimFloat);
+ __ MovS(destination.AsFpuRegister<FRegister>(), source.AsFpuRegister<FRegister>());
+ }
+ } else if (source.IsDoubleStackSlot()) {
+ DCHECK(Primitive::Is64BitType(dst_type));
+ __ LoadDFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
+ } else {
+ DCHECK(!Primitive::Is64BitType(dst_type));
+ DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
+ __ LoadSFromOffset(destination.AsFpuRegister<FRegister>(), SP, source.GetStackIndex());
+ }
+ } else if (destination.IsDoubleStackSlot()) {
+ int32_t dst_offset = destination.GetStackIndex();
+ if (source.IsRegisterPair()) {
+ __ StoreToOffset(kStoreDoubleword, source.AsRegisterPairLow<Register>(), SP, dst_offset);
+ } else if (source.IsFpuRegister()) {
+ __ StoreDToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset);
+ } else {
+ DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination;
+ __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
+ __ StoreToOffset(kStoreWord, TMP, SP, dst_offset);
+ __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex() + 4);
+ __ StoreToOffset(kStoreWord, TMP, SP, dst_offset + 4);
+ }
} else {
- DCHECK(source.IsDoubleStackSlot()) << "Cannot move from " << source << " to " << destination;
- __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
- __ StoreToOffset(kStoreWord, TMP, SP, off);
- __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex() + 4);
- __ StoreToOffset(kStoreWord, TMP, SP, off + 4);
+ DCHECK(destination.IsStackSlot()) << destination;
+ int32_t dst_offset = destination.GetStackIndex();
+ if (source.IsRegister()) {
+ __ StoreToOffset(kStoreWord, source.AsRegister<Register>(), SP, dst_offset);
+ } else if (source.IsFpuRegister()) {
+ __ StoreSToOffset(source.AsFpuRegister<FRegister>(), SP, dst_offset);
+ } else {
+ DCHECK(source.IsStackSlot()) << "Cannot move from " << source << " to " << destination;
+ __ LoadFromOffset(kLoadWord, TMP, SP, source.GetStackIndex());
+ __ StoreToOffset(kStoreWord, TMP, SP, dst_offset);
+ }
}
}
}
@@ -1584,14 +1611,15 @@ inline void CodeGeneratorMIPS::EmitPcRelativeLinkerPatches(
for (const PcRelativePatchInfo& info : infos) {
const DexFile& dex_file = info.target_dex_file;
size_t offset_or_index = info.offset_or_index;
- DCHECK(info.high_label.IsBound());
- uint32_t high_offset = __ GetLabelLocation(&info.high_label);
+ DCHECK(info.label.IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(&info.label);
// On R2 we use HMipsComputeBaseMethodAddress and patch relative to
// the assembler's base label used for PC-relative addressing.
- uint32_t pc_rel_offset = info.pc_rel_label.IsBound()
- ? __ GetLabelLocation(&info.pc_rel_label)
+ const PcRelativePatchInfo& info_high = info.patch_info_high ? *info.patch_info_high : info;
+ uint32_t pc_rel_offset = info_high.pc_rel_label.IsBound()
+ ? __ GetLabelLocation(&info_high.pc_rel_label)
: __ GetPcRelBaseLabelLocation();
- linker_patches->push_back(Factory(high_offset, &dex_file, pc_rel_offset, offset_or_index));
+ linker_patches->push_back(Factory(literal_offset, &dex_file, pc_rel_offset, offset_or_index));
}
}
@@ -1625,37 +1653,50 @@ void CodeGeneratorMIPS::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patch
}
CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeMethodPatch(
- MethodReference target_method) {
+ MethodReference target_method,
+ const PcRelativePatchInfo* info_high) {
return NewPcRelativePatch(*target_method.dex_file,
target_method.dex_method_index,
+ info_high,
&pc_relative_method_patches_);
}
CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewMethodBssEntryPatch(
- MethodReference target_method) {
+ MethodReference target_method,
+ const PcRelativePatchInfo* info_high) {
return NewPcRelativePatch(*target_method.dex_file,
target_method.dex_method_index,
+ info_high,
&method_bss_entry_patches_);
}
CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeTypePatch(
- const DexFile& dex_file, dex::TypeIndex type_index) {
- return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
+ const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high) {
+ return NewPcRelativePatch(dex_file, type_index.index_, info_high, &pc_relative_type_patches_);
}
CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewTypeBssEntryPatch(
- const DexFile& dex_file, dex::TypeIndex type_index) {
- return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_);
+ const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high) {
+ return NewPcRelativePatch(dex_file, type_index.index_, info_high, &type_bss_entry_patches_);
}
CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativeStringPatch(
- const DexFile& dex_file, dex::StringIndex string_index) {
- return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_);
+ const DexFile& dex_file,
+ dex::StringIndex string_index,
+ const PcRelativePatchInfo* info_high) {
+ return NewPcRelativePatch(dex_file, string_index.index_, info_high, &pc_relative_string_patches_);
}
CodeGeneratorMIPS::PcRelativePatchInfo* CodeGeneratorMIPS::NewPcRelativePatch(
- const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
- patches->emplace_back(dex_file, offset_or_index);
+ const DexFile& dex_file,
+ uint32_t offset_or_index,
+ const PcRelativePatchInfo* info_high,
+ ArenaDeque<PcRelativePatchInfo>* patches) {
+ patches->emplace_back(dex_file, offset_or_index, info_high);
return &patches->back();
}
@@ -1669,14 +1710,16 @@ Literal* CodeGeneratorMIPS::DeduplicateBootImageAddressLiteral(uint32_t address)
return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
}
-void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info,
+void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
Register out,
- Register base) {
+ Register base,
+ PcRelativePatchInfo* info_low) {
+ DCHECK(!info_high->patch_info_high);
DCHECK_NE(out, base);
if (GetInstructionSetFeatures().IsR6()) {
DCHECK_EQ(base, ZERO);
- __ Bind(&info->high_label);
- __ Bind(&info->pc_rel_label);
+ __ Bind(&info_high->label);
+ __ Bind(&info_high->pc_rel_label);
// Add the high half of a 32-bit offset to PC.
__ Auipc(out, /* placeholder */ 0x1234);
} else {
@@ -1685,18 +1728,20 @@ void CodeGeneratorMIPS::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo
// Generate a dummy PC-relative call to obtain PC.
__ Nal();
}
- __ Bind(&info->high_label);
+ __ Bind(&info_high->label);
__ Lui(out, /* placeholder */ 0x1234);
// If we emitted the NAL, bind the pc_rel_label, otherwise base is a register holding
// the HMipsComputeBaseMethodAddress which has its own label stored in MipsAssembler.
if (base == ZERO) {
- __ Bind(&info->pc_rel_label);
+ __ Bind(&info_high->pc_rel_label);
}
// Add the high half of a 32-bit offset to PC.
__ Addu(out, out, (base == ZERO) ? RA : base);
}
- // The immediately following instruction will add the sign-extended low half of the 32-bit
+ // A following instruction will add the sign-extended low half of the 32-bit
// offset to `out` (e.g. lw, jialc, addiu).
+ DCHECK_EQ(info_low->patch_info_high, info_high);
+ __ Bind(&info_low->label);
}
CodeGeneratorMIPS::JitPatchInfo* CodeGeneratorMIPS::NewJitRootStringPatch(
@@ -2285,7 +2330,7 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) {
Register lhs_low = locations->InAt(0).AsRegisterPairLow<Register>();
if (use_imm) {
if (shift_value == 0) {
- codegen_->Move64(locations->Out(), locations->InAt(0));
+ codegen_->MoveLocation(locations->Out(), locations->InAt(0), type);
} else if (shift_value < kMipsBitsPerWord) {
if (has_ins_rotr) {
if (instr->IsShl()) {
@@ -7140,10 +7185,12 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(
break;
case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
DCHECK(GetCompilerOptions().IsBootImage());
- PcRelativePatchInfo* info = NewPcRelativeMethodPatch(invoke->GetTargetMethod());
+ PcRelativePatchInfo* info_high = NewPcRelativeMethodPatch(invoke->GetTargetMethod());
+ PcRelativePatchInfo* info_low =
+ NewPcRelativeMethodPatch(invoke->GetTargetMethod(), info_high);
bool reordering = __ SetReorder(false);
Register temp_reg = temp.AsRegister<Register>();
- EmitPcRelativeAddressPlaceholderHigh(info, TMP, base_reg);
+ EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg, info_low);
__ Addiu(temp_reg, TMP, /* placeholder */ 0x5678);
__ SetReorder(reordering);
break;
@@ -7152,11 +7199,13 @@ void CodeGeneratorMIPS::GenerateStaticOrDirectCall(
__ LoadConst32(temp.AsRegister<Register>(), invoke->GetMethodAddress());
break;
case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
- PcRelativePatchInfo* info = NewMethodBssEntryPatch(
+ PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
+ PcRelativePatchInfo* info_low = NewMethodBssEntryPatch(
+ MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()), info_high);
Register temp_reg = temp.AsRegister<Register>();
bool reordering = __ SetReorder(false);
- EmitPcRelativeAddressPlaceholderHigh(info, TMP, base_reg);
+ EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base_reg, info_low);
__ Lw(temp_reg, TMP, /* placeholder */ 0x5678);
__ SetReorder(reordering);
break;
@@ -7286,11 +7335,8 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) {
if (load_kind == HLoadClass::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the type resolution or initialization and marking to save everything we need.
- // Request a temp to hold the BSS entry location for the slow path on R2
- // (no benefit for R6).
- if (!isR6) {
- locations->AddTemp(Location::RequiresRegister());
- }
+ // Request a temp to hold the BSS entry location for the slow path.
+ locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
@@ -7336,6 +7382,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF
? kWithoutReadBarrier
: kCompilerReadBarrierOption;
bool generate_null_check = false;
+ CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = nullptr;
switch (load_kind) {
case HLoadClass::LoadKind::kReferrersClass: {
DCHECK(!cls->CanCallRuntime());
@@ -7351,10 +7398,15 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
+ out,
+ base_or_current_method_reg,
+ info_low);
__ Addiu(out, out, /* placeholder */ 0x5678);
__ SetReorder(reordering);
break;
@@ -7370,24 +7422,18 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF
break;
}
case HLoadClass::LoadKind::kBssEntry: {
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
- codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
+ bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high);
constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier;
- if (isR6 || non_baker_read_barrier) {
- bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
- GenerateGcRootFieldLoad(cls, out_loc, out, /* placeholder */ 0x5678, read_barrier_option);
- __ SetReorder(reordering);
- } else {
- // On R2 save the BSS entry address in a temporary register instead of
- // recalculating it in the slow path.
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, temp, base_or_current_method_reg);
- __ Addiu(temp, temp, /* placeholder */ 0x5678);
- __ SetReorder(reordering);
- GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option);
- }
+ Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister<Register>();
+ bool reordering = __ SetReorder(false);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high,
+ temp,
+ base_or_current_method_reg,
+ info_low);
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* placeholder */ 0x5678, read_barrier_option);
+ __ SetReorder(reordering);
generate_null_check = true;
break;
}
@@ -7411,7 +7457,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF
if (generate_null_check || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck(), bss_info_high);
codegen_->AddSlowPath(slow_path);
if (generate_null_check) {
__ Beqz(out, slow_path->GetEntryLabel());
@@ -7476,11 +7522,8 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the pResolveString and marking to save everything we need.
- // Request a temp to hold the BSS entry location for the slow path on R2
- // (no benefit for R6).
- if (!isR6) {
- locations->AddTemp(Location::RequiresRegister());
- }
+ // Request a temp to hold the BSS entry location for the slow path.
+ locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
@@ -7516,10 +7559,15 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_
switch (load_kind) {
case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
+ out,
+ base_or_current_method_reg,
+ info_low);
__ Addiu(out, out, /* placeholder */ 0x5678);
__ SetReorder(reordering);
return; // No dex cache slow path.
@@ -7535,29 +7583,25 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
- CodeGeneratorMIPS::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_high =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
+ CodeGeneratorMIPS::PcRelativePatchInfo* info_low =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier;
- if (isR6 || non_baker_read_barrier) {
- bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out, base_or_current_method_reg);
- GenerateGcRootFieldLoad(load,
- out_loc,
- out,
- /* placeholder */ 0x5678,
- kCompilerReadBarrierOption);
- __ SetReorder(reordering);
- } else {
- // On R2 save the BSS entry address in a temporary register instead of
- // recalculating it in the slow path.
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- bool reordering = __ SetReorder(false);
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, temp, base_or_current_method_reg);
- __ Addiu(temp, temp, /* placeholder */ 0x5678);
- __ SetReorder(reordering);
- GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption);
- }
- SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load);
+ Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister<Register>();
+ bool reordering = __ SetReorder(false);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high,
+ temp,
+ base_or_current_method_reg,
+ info_low);
+ GenerateGcRootFieldLoad(load,
+ out_loc,
+ temp,
+ /* placeholder */ 0x5678,
+ kCompilerReadBarrierOption);
+ __ SetReorder(reordering);
+ SlowPathCodeMIPS* slow_path =
+ new (GetGraph()->GetArena()) LoadStringSlowPathMIPS(load, info_high);
codegen_->AddSlowPath(slow_path);
__ Beqz(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index e72e838dd9..c259ea3e48 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -368,8 +368,6 @@ class CodeGeneratorMIPS : public CodeGenerator {
void Bind(HBasicBlock* block) OVERRIDE;
- void Move32(Location destination, Location source);
- void Move64(Location destination, Location source);
void MoveConstant(Location location, HConstant* c);
size_t GetWordSize() const OVERRIDE { return kMipsWordSize; }
@@ -568,31 +566,68 @@ class CodeGeneratorMIPS : public CodeGenerator {
// The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays
// and boot image strings. The only difference is the interpretation of the offset_or_index.
+ // The 16-bit halves of the 32-bit PC-relative offset are patched separately, necessitating
+ // two patches/infos. There can be more than two patches/infos if the instruction supplying
+ // the high half is shared with e.g. a slow path, while the low half is supplied by separate
+ // instructions, e.g.:
+ // lui r1, high // patch
+ // addu r1, r1, rbase
+ // lw r2, low(r1) // patch
+ // beqz r2, slow_path
+ // back:
+ // ...
+ // slow_path:
+ // ...
+ // sw r2, low(r1) // patch
+ // b back
struct PcRelativePatchInfo {
- PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
- : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
- PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+ PcRelativePatchInfo(const DexFile& dex_file,
+ uint32_t off_or_idx,
+ const PcRelativePatchInfo* info_high)
+ : target_dex_file(dex_file),
+ offset_or_index(off_or_idx),
+ label(),
+ pc_rel_label(),
+ patch_info_high(info_high) { }
const DexFile& target_dex_file;
// Either the dex cache array element offset or the string/type index.
uint32_t offset_or_index;
- // Label for the instruction loading the most significant half of the offset that's added to PC
- // to form the base address (the least significant half is loaded with the instruction that
- // follows).
- MipsLabel high_label;
- // Label for the instruction corresponding to PC+0.
+ // Label for the instruction to patch.
+ MipsLabel label;
+ // Label for the instruction corresponding to PC+0. Not bound or used in low half patches.
+ // Not bound in high half patches on R2 when using HMipsComputeBaseMethodAddress.
+ // Bound in high half patches on R2 when using the NAL instruction instead of
+ // HMipsComputeBaseMethodAddress.
+ // Bound in high half patches on R6.
MipsLabel pc_rel_label;
+ // Pointer to the info for the high half patch or nullptr if this is the high half patch info.
+ const PcRelativePatchInfo* patch_info_high;
+
+ private:
+ PcRelativePatchInfo(PcRelativePatchInfo&& other) = delete;
+ DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo);
};
- PcRelativePatchInfo* NewPcRelativeMethodPatch(MethodReference target_method);
- PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method);
- PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
- PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index);
+ PcRelativePatchInfo* NewPcRelativeMethodPatch(MethodReference target_method,
+ const PcRelativePatchInfo* info_high = nullptr);
+ PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method,
+ const PcRelativePatchInfo* info_high = nullptr);
+ PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high = nullptr);
+ PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high = nullptr);
PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file,
- dex::StringIndex string_index);
+ dex::StringIndex string_index,
+ const PcRelativePatchInfo* info_high = nullptr);
Literal* DeduplicateBootImageAddressLiteral(uint32_t address);
- void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, Register out, Register base);
+ void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
+ Register out,
+ Register base,
+ PcRelativePatchInfo* info_low);
// The JitPatchInfo is used for JIT string and class loads.
struct JitPatchInfo {
@@ -627,6 +662,7 @@ class CodeGeneratorMIPS : public CodeGenerator {
Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
uint32_t offset_or_index,
+ const PcRelativePatchInfo* info_high,
ArenaDeque<PcRelativePatchInfo>* patches);
template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index e4f1cbd600..5fb8755086 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -164,19 +164,42 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
LoadClassSlowPathMIPS64(HLoadClass* cls,
HInstruction* at,
uint32_t dex_pc,
- bool do_clinit)
- : SlowPathCodeMIPS64(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) {
+ bool do_clinit,
+ const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = nullptr)
+ : SlowPathCodeMIPS64(at),
+ cls_(cls),
+ dex_pc_(dex_pc),
+ do_clinit_(do_clinit),
+ bss_info_high_(bss_info_high) {
DCHECK(at->IsLoadClass() || at->IsClinitCheck());
}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
LocationSummary* locations = instruction_->GetLocations();
+ Location out = locations->Out();
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
-
+ const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier);
+ InvokeRuntimeCallingConvention calling_convention;
+ DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
+ const bool is_load_class_bss_entry =
+ (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
- InvokeRuntimeCallingConvention calling_convention;
+ // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ GpuRegister entry_address = kNoGpuRegister;
+ if (is_load_class_bss_entry && baker_or_no_read_barriers) {
+ GpuRegister temp = locations->GetTemp(0).AsRegister<GpuRegister>();
+ bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0));
+ // In the unlucky case that `temp` is A0, we preserve the address in `out` across the
+ // kSaveEverything call.
+ entry_address = temp_is_a0 ? out.AsRegister<GpuRegister>() : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_a0) {
+ __ Move(entry_address, temp);
+ }
+ }
+
dex::TypeIndex type_index = cls_->GetTypeIndex();
__ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_);
QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
@@ -188,8 +211,20 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
}
+ // For HLoadClass/kBssEntry, store the resolved class to the BSS entry.
+ if (is_load_class_bss_entry && baker_or_no_read_barriers) {
+ // The class entry address was preserved in `entry_address` thanks to kSaveEverything.
+ DCHECK(bss_info_high_);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_);
+ __ Bind(&info_low->label);
+ __ StoreToOffset(kStoreWord,
+ calling_convention.GetRegisterAt(0),
+ entry_address,
+ /* placeholder */ 0x5678);
+ }
+
// Move the class to the desired location.
- Location out = locations->Out();
if (out.IsValid()) {
DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Primitive::Type type = instruction_->GetType();
@@ -197,16 +232,18 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
type);
}
-
RestoreLiveRegisters(codegen, locations);
- // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry.
- DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
- if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
- DCHECK(out.IsValid());
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+
+ // For HLoadClass/kBssEntry, store the resolved class to the BSS entry.
+ if (is_load_class_bss_entry && !baker_or_no_read_barriers) {
+ // For non-Baker read barriers we need to re-calculate the address of
+ // the class entry.
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
- __ Sw(out.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high);
+ mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, info_low);
+ __ StoreToOffset(kStoreWord, out.AsRegister<GpuRegister>(), TMP, /* placeholder */ 0x5678);
}
__ Bc(GetExitLabel());
}
@@ -223,50 +260,94 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 {
// Whether to initialize the class.
const bool do_clinit_;
+ // Pointer to the high half PC-relative patch info for HLoadClass/kBssEntry.
+ const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high_;
+
DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS64);
};
class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 {
public:
- explicit LoadStringSlowPathMIPS64(HLoadString* instruction) : SlowPathCodeMIPS64(instruction) {}
+ explicit LoadStringSlowPathMIPS64(HLoadString* instruction,
+ const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high)
+ : SlowPathCodeMIPS64(instruction), bss_info_high_(bss_info_high) {}
void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ DCHECK(instruction_->IsLoadString());
+ DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
LocationSummary* locations = instruction_->GetLocations();
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
+ HLoadString* load = instruction_->AsLoadString();
+ const dex::StringIndex string_index = load->GetStringIndex();
+ GpuRegister out = locations->Out().AsRegister<GpuRegister>();
CodeGeneratorMIPS64* mips64_codegen = down_cast<CodeGeneratorMIPS64*>(codegen);
-
+ const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier);
+ InvokeRuntimeCallingConvention calling_convention;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
- InvokeRuntimeCallingConvention calling_convention;
- HLoadString* load = instruction_->AsLoadString();
- const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
+ // For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry.
+ GpuRegister entry_address = kNoGpuRegister;
+ if (baker_or_no_read_barriers) {
+ GpuRegister temp = locations->GetTemp(0).AsRegister<GpuRegister>();
+ bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0));
+ // In the unlucky case that `temp` is A0, we preserve the address in `out` across the
+ // kSaveEverything call.
+ entry_address = temp_is_a0 ? out : temp;
+ DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0));
+ if (temp_is_a0) {
+ __ Move(entry_address, temp);
+ }
+ }
+
__ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_);
mips64_codegen->InvokeRuntime(kQuickResolveString,
instruction_,
instruction_->GetDexPc(),
this);
CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
+
+ // Store the resolved string to the BSS entry.
+ if (baker_or_no_read_barriers) {
+ // The string entry address was preserved in `entry_address` thanks to kSaveEverything.
+ DCHECK(bss_info_high_);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(),
+ string_index,
+ bss_info_high_);
+ __ Bind(&info_low->label);
+ __ StoreToOffset(kStoreWord,
+ calling_convention.GetRegisterAt(0),
+ entry_address,
+ /* placeholder */ 0x5678);
+ }
+
Primitive::Type type = instruction_->GetType();
mips64_codegen->MoveLocation(locations->Out(),
Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
type);
-
RestoreLiveRegisters(codegen, locations);
- // Store the resolved String to the BSS entry.
- GpuRegister out = locations->Out().AsRegister<GpuRegister>();
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
- mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
- mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
- __ Sw(out, AT, /* placeholder */ 0x5678);
-
+ // Store the resolved string to the BSS entry.
+ if (!baker_or_no_read_barriers) {
+ // For non-Baker read barriers we need to re-calculate the address of
+ // the string entry.
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
+ mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index, info_high);
+ mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, info_low);
+ __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678);
+ }
__ Bc(GetExitLabel());
}
const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; }
private:
+ // Pointer to the high half PC-relative patch info.
+ const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high_;
+
DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64);
};
@@ -1431,9 +1512,11 @@ inline void CodeGeneratorMIPS64::EmitPcRelativeLinkerPatches(
for (const PcRelativePatchInfo& info : infos) {
const DexFile& dex_file = info.target_dex_file;
size_t offset_or_index = info.offset_or_index;
- DCHECK(info.pc_rel_label.IsBound());
- uint32_t pc_rel_offset = __ GetLabelLocation(&info.pc_rel_label);
- linker_patches->push_back(Factory(pc_rel_offset, &dex_file, pc_rel_offset, offset_or_index));
+ DCHECK(info.label.IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(&info.label);
+ const PcRelativePatchInfo& info_high = info.patch_info_high ? *info.patch_info_high : info;
+ uint32_t pc_rel_offset = __ GetLabelLocation(&info_high.label);
+ linker_patches->push_back(Factory(literal_offset, &dex_file, pc_rel_offset, offset_or_index));
}
}
@@ -1467,37 +1550,50 @@ void CodeGeneratorMIPS64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeMethodPatch(
- MethodReference target_method) {
+ MethodReference target_method,
+ const PcRelativePatchInfo* info_high) {
return NewPcRelativePatch(*target_method.dex_file,
target_method.dex_method_index,
+ info_high,
&pc_relative_method_patches_);
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewMethodBssEntryPatch(
- MethodReference target_method) {
+ MethodReference target_method,
+ const PcRelativePatchInfo* info_high) {
return NewPcRelativePatch(*target_method.dex_file,
target_method.dex_method_index,
+ info_high,
&method_bss_entry_patches_);
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeTypePatch(
- const DexFile& dex_file, dex::TypeIndex type_index) {
- return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
+ const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high) {
+ return NewPcRelativePatch(dex_file, type_index.index_, info_high, &pc_relative_type_patches_);
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewTypeBssEntryPatch(
- const DexFile& dex_file, dex::TypeIndex type_index) {
- return NewPcRelativePatch(dex_file, type_index.index_, &type_bss_entry_patches_);
+ const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high) {
+ return NewPcRelativePatch(dex_file, type_index.index_, info_high, &type_bss_entry_patches_);
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeStringPatch(
- const DexFile& dex_file, dex::StringIndex string_index) {
- return NewPcRelativePatch(dex_file, string_index.index_, &pc_relative_string_patches_);
+ const DexFile& dex_file,
+ dex::StringIndex string_index,
+ const PcRelativePatchInfo* info_high) {
+ return NewPcRelativePatch(dex_file, string_index.index_, info_high, &pc_relative_string_patches_);
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativePatch(
- const DexFile& dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
- patches->emplace_back(dex_file, offset_or_index);
+ const DexFile& dex_file,
+ uint32_t offset_or_index,
+ const PcRelativePatchInfo* info_high,
+ ArenaDeque<PcRelativePatchInfo>* patches) {
+ patches->emplace_back(dex_file, offset_or_index, info_high);
return &patches->back();
}
@@ -1517,13 +1613,17 @@ Literal* CodeGeneratorMIPS64::DeduplicateBootImageAddressLiteral(uint64_t addres
return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), &uint32_literals_);
}
-void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info,
- GpuRegister out) {
- __ Bind(&info->pc_rel_label);
+void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
+ GpuRegister out,
+ PcRelativePatchInfo* info_low) {
+ DCHECK(!info_high->patch_info_high);
+ __ Bind(&info_high->label);
// Add the high half of a 32-bit offset to PC.
__ Auipc(out, /* placeholder */ 0x1234);
- // The immediately following instruction will add the sign-extended low half of the 32-bit
+ // A following instruction will add the sign-extended low half of the 32-bit
// offset to `out` (e.g. ld, jialc, daddiu).
+ DCHECK_EQ(info_low->patch_info_high, info_high);
+ __ Bind(&info_low->label);
}
Literal* CodeGeneratorMIPS64::DeduplicateJitStringLiteral(const DexFile& dex_file,
@@ -4940,9 +5040,11 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(
break;
case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
DCHECK(GetCompilerOptions().IsBootImage());
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
NewPcRelativeMethodPatch(invoke->GetTargetMethod());
- EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ NewPcRelativeMethodPatch(invoke->GetTargetMethod(), info_high);
+ EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
__ Daddiu(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
break;
}
@@ -4952,9 +5054,11 @@ void CodeGeneratorMIPS64::GenerateStaticOrDirectCall(
DeduplicateUint64Literal(invoke->GetMethodAddress()));
break;
case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
- PcRelativePatchInfo* info = NewMethodBssEntryPatch(
+ PcRelativePatchInfo* info_high = NewMethodBssEntryPatch(
MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
- EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ PcRelativePatchInfo* info_low = NewMethodBssEntryPatch(
+ MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()), info_high);
+ EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
__ Ld(temp.AsRegister<GpuRegister>(), AT, /* placeholder */ 0x5678);
break;
}
@@ -5071,12 +5175,14 @@ void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
if (load_kind == HLoadClass::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the type resolution or initialization and marking to save everything we need.
+ // Request a temp to hold the BSS entry location for the slow path.
+ locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetCustomSlowPathCallerSaves(caller_saves);
} else {
- // For non-Baker read barrier we have a temp-clobbering call.
+ // For non-Baker read barriers we have a temp-clobbering call.
}
}
}
@@ -5104,6 +5210,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S
? kWithoutReadBarrier
: kCompilerReadBarrierOption;
bool generate_null_check = false;
+ CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = nullptr;
switch (load_kind) {
case HLoadClass::LoadKind::kReferrersClass:
DCHECK(!cls->CanCallRuntime());
@@ -5118,9 +5225,11 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex(), info_high);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
__ Daddiu(out, AT, /* placeholder */ 0x5678);
break;
}
@@ -5135,10 +5244,15 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S
break;
}
case HLoadClass::LoadKind::kBssEntry: {
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
- codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out);
- GenerateGcRootFieldLoad(cls, out_loc, out, /* placeholder */ 0x5678, read_barrier_option);
+ bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high);
+ constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier;
+ GpuRegister temp = non_baker_read_barrier
+ ? out
+ : locations->GetTemp(0).AsRegister<GpuRegister>();
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp, info_low);
+ GenerateGcRootFieldLoad(cls, out_loc, temp, /* placeholder */ 0x5678, read_barrier_option);
generate_null_check = true;
break;
}
@@ -5159,7 +5273,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S
if (generate_null_check || cls->MustGenerateClinitCheck()) {
DCHECK(cls->CanCallRuntime());
SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
- cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck(), bss_info_high);
codegen_->AddSlowPath(slow_path);
if (generate_null_check) {
__ Beqzc(out, slow_path->GetEntryLabel());
@@ -5207,12 +5321,14 @@ void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
if (load_kind == HLoadString::LoadKind::kBssEntry) {
if (!kUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the pResolveString and marking to save everything we need.
+ // Request a temp to hold the BSS entry location for the slow path.
+ locations->AddTemp(Location::RequiresRegister());
RegisterSet caller_saves = RegisterSet::Empty();
InvokeRuntimeCallingConvention calling_convention;
caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetCustomSlowPathCallerSaves(caller_saves);
} else {
- // For non-Baker read barrier we have a temp-clobbering call.
+ // For non-Baker read barriers we have a temp-clobbering call.
}
}
}
@@ -5229,9 +5345,11 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA
switch (load_kind) {
case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, AT, info_low);
__ Daddiu(out, AT, /* placeholder */ 0x5678);
return; // No dex cache slow path.
}
@@ -5246,15 +5364,22 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA
}
case HLoadString::LoadKind::kBssEntry: {
DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
- CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_high =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex());
- codegen_->EmitPcRelativeAddressPlaceholderHigh(info, out);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info_low =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex(), info_high);
+ constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier;
+ GpuRegister temp = non_baker_read_barrier
+ ? out
+ : locations->GetTemp(0).AsRegister<GpuRegister>();
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp, info_low);
GenerateGcRootFieldLoad(load,
out_loc,
- out,
+ temp,
/* placeholder */ 0x5678,
kCompilerReadBarrierOption);
- SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
+ SlowPathCodeMIPS64* slow_path =
+ new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load, info_high);
codegen_->AddSlowPath(slow_path);
__ Beqzc(out, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 6260c73614..b6209735b5 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -538,29 +538,59 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
// The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays,
// boot image strings and method calls. The only difference is the interpretation of
// the offset_or_index.
+ // The 16-bit halves of the 32-bit PC-relative offset are patched separately, necessitating
+ // two patches/infos. There can be more than two patches/infos if the instruction supplying
+ // the high half is shared with e.g. a slow path, while the low half is supplied by separate
+ // instructions, e.g.:
+ // auipc r1, high // patch
+ // lwu r2, low(r1) // patch
+ // beqzc r2, slow_path
+ // back:
+ // ...
+ // slow_path:
+ // ...
+ // sw r2, low(r1) // patch
+ // bc back
struct PcRelativePatchInfo {
- PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx)
- : target_dex_file(dex_file), offset_or_index(off_or_idx) { }
- PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
+ PcRelativePatchInfo(const DexFile& dex_file,
+ uint32_t off_or_idx,
+ const PcRelativePatchInfo* info_high)
+ : target_dex_file(dex_file),
+ offset_or_index(off_or_idx),
+ label(),
+ patch_info_high(info_high) { }
const DexFile& target_dex_file;
// Either the dex cache array element offset or the string/type/method index.
uint32_t offset_or_index;
- // Label for the auipc instruction.
- Mips64Label pc_rel_label;
+ // Label for the instruction to patch.
+ Mips64Label label;
+ // Pointer to the info for the high half patch or nullptr if this is the high half patch info.
+ const PcRelativePatchInfo* patch_info_high;
+
+ private:
+ PcRelativePatchInfo(PcRelativePatchInfo&& other) = delete;
+ DISALLOW_COPY_AND_ASSIGN(PcRelativePatchInfo);
};
- PcRelativePatchInfo* NewPcRelativeMethodPatch(MethodReference target_method);
- PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method);
- PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
- PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file, dex::TypeIndex type_index);
+ PcRelativePatchInfo* NewPcRelativeMethodPatch(MethodReference target_method,
+ const PcRelativePatchInfo* info_high = nullptr);
+ PcRelativePatchInfo* NewMethodBssEntryPatch(MethodReference target_method,
+ const PcRelativePatchInfo* info_high = nullptr);
+ PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high = nullptr);
+ PcRelativePatchInfo* NewTypeBssEntryPatch(const DexFile& dex_file,
+ dex::TypeIndex type_index,
+ const PcRelativePatchInfo* info_high = nullptr);
PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file,
- dex::StringIndex string_index);
- PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file,
- uint32_t method_index);
+ dex::StringIndex string_index,
+ const PcRelativePatchInfo* info_high = nullptr);
Literal* DeduplicateBootImageAddressLiteral(uint64_t address);
- void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, GpuRegister out);
+ void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info_high,
+ GpuRegister out,
+ PcRelativePatchInfo* info_low);
void PatchJitRootUse(uint8_t* code,
const uint8_t* roots_data,
@@ -588,6 +618,7 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
PcRelativePatchInfo* NewPcRelativePatch(const DexFile& dex_file,
uint32_t offset_or_index,
+ const PcRelativePatchInfo* info_high,
ArenaDeque<PcRelativePatchInfo>* patches);
template <LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 53f314ec40..527691d9d9 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -15,19 +15,62 @@
*/
#include "code_generator_arm_vixl.h"
+#include "mirror/array-inl.h"
+
+namespace vixl32 = vixl::aarch32;
+using namespace vixl32; // NOLINT(build/namespaces)
namespace art {
namespace arm {
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
+using helpers::DRegisterFrom;
+using helpers::Int64ConstantFrom;
+using helpers::InputDRegisterAt;
+using helpers::InputRegisterAt;
+using helpers::OutputDRegister;
+using helpers::RegisterFrom;
+
+#define __ GetVIXLAssembler()->
void LocationsBuilderARMVIXL::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresFpuRegister());
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void InstructionCodeGeneratorARMVIXL::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vdup(Untyped8, dst, InputRegisterAt(instruction, 0));
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vdup(Untyped16, dst, InputRegisterAt(instruction, 0));
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vdup(Untyped32, dst, InputRegisterAt(instruction, 0));
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
@@ -51,13 +94,17 @@ static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* in
LocationSummary* locations = new (arena) LocationSummary(instruction);
switch (instruction->GetPackedType()) {
case Primitive::kPrimBoolean:
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(),
+ instruction->IsVecNot() ? Location::kOutputOverlap
+ : Location::kNoOutputOverlap);
+ break;
case Primitive::kPrimByte:
case Primitive::kPrimChar:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- DCHECK(locations);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
@@ -78,7 +125,27 @@ void LocationsBuilderARMVIXL::VisitVecNeg(HVecNeg* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecNeg(HVecNeg* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister src = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vneg(DataTypeValue::S8, dst, src);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vneg(DataTypeValue::S16, dst, src);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vneg(DataTypeValue::S32, dst, src);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecAbs(HVecAbs* instruction) {
@@ -86,7 +153,27 @@ void LocationsBuilderARMVIXL::VisitVecAbs(HVecAbs* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecAbs(HVecAbs* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister src = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vabs(DataTypeValue::S8, dst, src);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vabs(DataTypeValue::S16, dst, src);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vabs(DataTypeValue::S32, dst, src);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecNot(HVecNot* instruction) {
@@ -94,7 +181,25 @@ void LocationsBuilderARMVIXL::VisitVecNot(HVecNot* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecNot(HVecNot* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister src = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean: // special case boolean-not
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vmov(I8, dst, 1);
+ __ Veor(dst, dst, src);
+ break;
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ __ Vmvn(I8, dst, src); // lanes do not matter
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector binary operations.
@@ -106,9 +211,9 @@ static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation*
case Primitive::kPrimChar:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
- case Primitive::kPrimFloat:
- case Primitive::kPrimDouble:
- DCHECK(locations);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
@@ -121,7 +226,28 @@ void LocationsBuilderARMVIXL::VisitVecAdd(HVecAdd* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecAdd(HVecAdd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vadd(I8, dst, lhs, rhs);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vadd(I16, dst, lhs, rhs);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vadd(I32, dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
@@ -129,7 +255,40 @@ void LocationsBuilderARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecHalvingAdd(HVecHalvingAdd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ instruction->IsRounded()
+ ? __ Vrhadd(DataTypeValue::U8, dst, lhs, rhs)
+ : __ Vhadd(DataTypeValue::U8, dst, lhs, rhs);
+ } else {
+ instruction->IsRounded()
+ ? __ Vrhadd(DataTypeValue::S8, dst, lhs, rhs)
+ : __ Vhadd(DataTypeValue::S8, dst, lhs, rhs);
+ }
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ instruction->IsRounded()
+ ? __ Vrhadd(DataTypeValue::U16, dst, lhs, rhs)
+ : __ Vhadd(DataTypeValue::U16, dst, lhs, rhs);
+ } else {
+ instruction->IsRounded()
+ ? __ Vrhadd(DataTypeValue::S16, dst, lhs, rhs)
+ : __ Vhadd(DataTypeValue::S16, dst, lhs, rhs);
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecSub(HVecSub* instruction) {
@@ -137,7 +296,28 @@ void LocationsBuilderARMVIXL::VisitVecSub(HVecSub* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecSub(HVecSub* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vsub(I8, dst, lhs, rhs);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vsub(I16, dst, lhs, rhs);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vsub(I32, dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecMul(HVecMul* instruction) {
@@ -145,7 +325,28 @@ void LocationsBuilderARMVIXL::VisitVecMul(HVecMul* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecMul(HVecMul* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vmul(I8, dst, lhs, rhs);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vmul(I16, dst, lhs, rhs);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vmul(I32, dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecDiv(HVecDiv* instruction) {
@@ -161,7 +362,40 @@ void LocationsBuilderARMVIXL::VisitVecMin(HVecMin* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecMin(HVecMin* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ __ Vmin(DataTypeValue::U8, dst, lhs, rhs);
+ } else {
+ __ Vmin(DataTypeValue::S8, dst, lhs, rhs);
+ }
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ __ Vmin(DataTypeValue::U16, dst, lhs, rhs);
+ } else {
+ __ Vmin(DataTypeValue::S16, dst, lhs, rhs);
+ }
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ __ Vmin(DataTypeValue::U32, dst, lhs, rhs);
+ } else {
+ __ Vmin(DataTypeValue::S32, dst, lhs, rhs);
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecMax(HVecMax* instruction) {
@@ -169,7 +403,40 @@ void LocationsBuilderARMVIXL::VisitVecMax(HVecMax* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecMax(HVecMax* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ __ Vmax(DataTypeValue::U8, dst, lhs, rhs);
+ } else {
+ __ Vmax(DataTypeValue::S8, dst, lhs, rhs);
+ }
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ __ Vmax(DataTypeValue::U16, dst, lhs, rhs);
+ } else {
+ __ Vmax(DataTypeValue::S16, dst, lhs, rhs);
+ }
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ if (instruction->IsUnsigned()) {
+ __ Vmax(DataTypeValue::U32, dst, lhs, rhs);
+ } else {
+ __ Vmax(DataTypeValue::S32, dst, lhs, rhs);
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecAnd(HVecAnd* instruction) {
@@ -177,7 +444,22 @@ void LocationsBuilderARMVIXL::VisitVecAnd(HVecAnd* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecAnd(HVecAnd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ __ Vand(I8, dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecAndNot(HVecAndNot* instruction) {
@@ -193,7 +475,22 @@ void LocationsBuilderARMVIXL::VisitVecOr(HVecOr* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecOr(HVecOr* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ __ Vorr(I8, dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecXor(HVecXor* instruction) {
@@ -201,7 +498,22 @@ void LocationsBuilderARMVIXL::VisitVecXor(HVecXor* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecXor(HVecXor* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister rhs = DRegisterFrom(locations->InAt(1));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ __ Veor(I8, dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector shift operations.
@@ -212,8 +524,9 @@ static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation*
case Primitive::kPrimChar:
case Primitive::kPrimShort:
case Primitive::kPrimInt:
- case Primitive::kPrimLong:
- DCHECK(locations);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+ locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
break;
default:
LOG(FATAL) << "Unsupported SIMD type";
@@ -226,7 +539,28 @@ void LocationsBuilderARMVIXL::VisitVecShl(HVecShl* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecShl(HVecShl* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vshl(I8, dst, lhs, value);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vshl(I16, dst, lhs, value);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vshl(I32, dst, lhs, value);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecShr(HVecShr* instruction) {
@@ -234,7 +568,28 @@ void LocationsBuilderARMVIXL::VisitVecShr(HVecShr* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecShr(HVecShr* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vshr(DataTypeValue::S8, dst, lhs, value);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vshr(DataTypeValue::S16, dst, lhs, value);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vshr(DataTypeValue::S32, dst, lhs, value);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecUShr(HVecUShr* instruction) {
@@ -242,7 +597,28 @@ void LocationsBuilderARMVIXL::VisitVecUShr(HVecUShr* instruction) {
}
void InstructionCodeGeneratorARMVIXL::VisitVecUShr(HVecUShr* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::DRegister lhs = DRegisterFrom(locations->InAt(0));
+ vixl32::DRegister dst = DRegisterFrom(locations->Out());
+ int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ __ Vshr(DataTypeValue::U8, dst, lhs, value);
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ __ Vshr(DataTypeValue::U16, dst, lhs, value);
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ __ Vshr(DataTypeValue::U32, dst, lhs, value);
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
@@ -253,20 +629,187 @@ void InstructionCodeGeneratorARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAcc
LOG(FATAL) << "No SIMD for " << instr->GetId();
}
+// Return whether the vector memory access operation is guaranteed to be word-aligned (ARM word
+// size equals to 4).
+static bool IsWordAligned(HVecMemoryOperation* instruction) {
+ return instruction->GetAlignment().IsAlignedAt(4u);
+}
+
+// Helper to set up locations for vector memory operations.
+static void CreateVecMemLocations(ArenaAllocator* arena,
+ HVecMemoryOperation* instruction,
+ bool is_load) {
+ LocationSummary* locations = new (arena) LocationSummary(instruction);
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimInt:
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+ if (is_load) {
+ locations->SetOut(Location::RequiresFpuRegister());
+ } else {
+ locations->SetInAt(2, Location::RequiresFpuRegister());
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
+}
+
+// Helper to set up locations for vector memory operations. Returns the memory operand and,
+// if used, sets the output parameter scratch to a temporary register used in this operand,
+// so that the client can release it right after the memory operand use.
+MemOperand InstructionCodeGeneratorARMVIXL::VecAddress(
+ HVecMemoryOperation* instruction,
+ UseScratchRegisterScope* temps_scope,
+ /*out*/ vixl32::Register* scratch) {
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::Register base = InputRegisterAt(instruction, 0);
+
+ Location index = locations->InAt(1);
+ size_t size = Primitive::ComponentSize(instruction->GetPackedType());
+ uint32_t offset = mirror::Array::DataOffset(size).Uint32Value();
+ size_t shift = ComponentSizeShiftWidth(size);
+
+ // HIntermediateAddress optimization is only applied for scalar ArrayGet and ArraySet.
+ DCHECK(!instruction->InputAt(0)->IsIntermediateAddress());
+
+ if (index.IsConstant()) {
+ offset += Int64ConstantFrom(index) << shift;
+ return MemOperand(base, offset);
+ } else {
+ *scratch = temps_scope->Acquire();
+ __ Add(*scratch, base, Operand(RegisterFrom(index), ShiftType::LSL, shift));
+
+ return MemOperand(*scratch, offset);
+ }
+}
+
+AlignedMemOperand InstructionCodeGeneratorARMVIXL::VecAddressUnaligned(
+ HVecMemoryOperation* instruction,
+ UseScratchRegisterScope* temps_scope,
+ /*out*/ vixl32::Register* scratch) {
+ LocationSummary* locations = instruction->GetLocations();
+ vixl32::Register base = InputRegisterAt(instruction, 0);
+
+ Location index = locations->InAt(1);
+ size_t size = Primitive::ComponentSize(instruction->GetPackedType());
+ uint32_t offset = mirror::Array::DataOffset(size).Uint32Value();
+ size_t shift = ComponentSizeShiftWidth(size);
+
+ // HIntermediateAddress optimization is only applied for scalar ArrayGet and ArraySet.
+ DCHECK(!instruction->InputAt(0)->IsIntermediateAddress());
+
+ if (index.IsConstant()) {
+ offset += Int64ConstantFrom(index) << shift;
+ __ Add(*scratch, base, offset);
+ } else {
+ *scratch = temps_scope->Acquire();
+ __ Add(*scratch, base, offset);
+ __ Add(*scratch, *scratch, Operand(RegisterFrom(index), ShiftType::LSL, shift));
+ }
+ return AlignedMemOperand(*scratch, kNoAlignment);
+}
+
void LocationsBuilderARMVIXL::VisitVecLoad(HVecLoad* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ true);
}
void InstructionCodeGeneratorARMVIXL::VisitVecLoad(HVecLoad* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ vixl32::DRegister reg = OutputDRegister(instruction);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register scratch;
+
+ DCHECK(instruction->GetPackedType() != Primitive::kPrimChar || !instruction->IsStringCharAt());
+
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ if (IsWordAligned(instruction)) {
+ __ Vldr(reg, VecAddress(instruction, &temps, &scratch));
+ } else {
+ __ Vld1(Untyped8,
+ NeonRegisterList(reg, kMultipleLanes),
+ VecAddressUnaligned(instruction, &temps, &scratch));
+ }
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ if (IsWordAligned(instruction)) {
+ __ Vldr(reg, VecAddress(instruction, &temps, &scratch));
+ } else {
+ __ Vld1(Untyped16,
+ NeonRegisterList(reg, kMultipleLanes),
+ VecAddressUnaligned(instruction, &temps, &scratch));
+ }
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ if (IsWordAligned(instruction)) {
+ __ Vldr(reg, VecAddress(instruction, &temps, &scratch));
+ } else {
+ __ Vld1(Untyped32,
+ NeonRegisterList(reg, kMultipleLanes),
+ VecAddressUnaligned(instruction, &temps, &scratch));
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
void LocationsBuilderARMVIXL::VisitVecStore(HVecStore* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ false);
}
void InstructionCodeGeneratorARMVIXL::VisitVecStore(HVecStore* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ vixl32::DRegister reg = InputDRegisterAt(instruction, 2);
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Register scratch;
+ switch (instruction->GetPackedType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ DCHECK_EQ(8u, instruction->GetVectorLength());
+ if (IsWordAligned(instruction)) {
+ __ Vstr(reg, VecAddress(instruction, &temps, &scratch));
+ } else {
+ __ Vst1(Untyped8,
+ NeonRegisterList(reg, kMultipleLanes),
+ VecAddressUnaligned(instruction, &temps, &scratch));
+ }
+ break;
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ if (IsWordAligned(instruction)) {
+ __ Vstr(reg, VecAddress(instruction, &temps, &scratch));
+ } else {
+ __ Vst1(Untyped16,
+ NeonRegisterList(reg, kMultipleLanes),
+ VecAddressUnaligned(instruction, &temps, &scratch));
+ }
+ break;
+ case Primitive::kPrimInt:
+ DCHECK_EQ(2u, instruction->GetVectorLength());
+ if (IsWordAligned(instruction)) {
+ __ Vstr(reg, VecAddress(instruction, &temps, &scratch));
+ } else {
+ __ Vst1(Untyped32,
+ NeonRegisterList(reg, kMultipleLanes),
+ VecAddressUnaligned(instruction, &temps, &scratch));
+ }
+ break;
+ default:
+ LOG(FATAL) << "Unsupported SIMD type";
+ UNREACHABLE();
+ }
}
#undef __
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index c0ec58f824..f35aace3a9 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -373,21 +373,23 @@ bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) co
bool InductionVarRange::IsUnitStride(HInstruction* context,
HInstruction* instruction,
+ HGraph* graph,
/*out*/ HInstruction** offset) const {
HLoopInformation* loop = nullptr;
HInductionVarAnalysis::InductionInfo* info = nullptr;
HInductionVarAnalysis::InductionInfo* trip = nullptr;
if (HasInductionInfo(context, instruction, &loop, &info, &trip)) {
if (info->induction_class == HInductionVarAnalysis::kLinear &&
- info->op_b->operation == HInductionVarAnalysis::kFetch &&
!HInductionVarAnalysis::IsNarrowingLinear(info)) {
int64_t stride_value = 0;
if (IsConstant(info->op_a, kExact, &stride_value) && stride_value == 1) {
int64_t off_value = 0;
- if (IsConstant(info->op_b, kExact, &off_value) && off_value == 0) {
- *offset = nullptr;
- } else {
+ if (IsConstant(info->op_b, kExact, &off_value)) {
+ *offset = graph->GetConstant(info->op_b->type, off_value);
+ } else if (info->op_b->operation == HInductionVarAnalysis::kFetch) {
*offset = info->op_b->fetch;
+ } else {
+ return false;
}
return true;
}
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index a8ee829d08..ab1772bf15 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -163,6 +163,7 @@ class InductionVarRange {
*/
bool IsUnitStride(HInstruction* context,
HInstruction* instruction,
+ HGraph* graph,
/*out*/ HInstruction** offset) const;
/**
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index d01d3146fc..67d2093829 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -770,8 +770,8 @@ TEST_F(InductionVarRangeTest, ConstantTripCountUp) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(1000, tc);
HInstruction* offset = nullptr;
- EXPECT_TRUE(range_.IsUnitStride(phi, phi, &offset));
- EXPECT_TRUE(offset == nullptr);
+ EXPECT_TRUE(range_.IsUnitStride(phi, phi, graph_, &offset));
+ ExpectInt(0, offset);
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
@@ -826,7 +826,7 @@ TEST_F(InductionVarRangeTest, ConstantTripCountDown) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(1000, tc);
HInstruction* offset = nullptr;
- EXPECT_FALSE(range_.IsUnitStride(phi, phi, &offset));
+ EXPECT_FALSE(range_.IsUnitStride(phi, phi, graph_, &offset));
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
@@ -908,8 +908,8 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountUp) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(0, tc); // unknown
HInstruction* offset = nullptr;
- EXPECT_TRUE(range_.IsUnitStride(phi, phi, &offset));
- EXPECT_TRUE(offset == nullptr);
+ EXPECT_TRUE(range_.IsUnitStride(phi, phi, graph_, &offset));
+ ExpectInt(0, offset);
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
@@ -994,7 +994,7 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountDown) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(0, tc); // unknown
HInstruction* offset = nullptr;
- EXPECT_FALSE(range_.IsUnitStride(phi, phi, &offset));
+ EXPECT_FALSE(range_.IsUnitStride(phi, phi, graph_, &offset));
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc
index f2ee345c8c..5a8ac59195 100644
--- a/compiler/optimizing/load_store_analysis.cc
+++ b/compiler/optimizing/load_store_analysis.cc
@@ -22,6 +22,117 @@ namespace art {
// The number of heap locations for most of the methods stays below this threshold.
constexpr size_t kMaxNumberOfHeapLocations = 32;
+// Check if array indices array[idx1 +/- CONST] and array[idx2] MAY alias.
+static bool BinaryOpAndIndexMayAlias(const HBinaryOperation* idx1, const HInstruction* idx2) {
+ DCHECK(idx1 != nullptr);
+ DCHECK(idx2 != nullptr);
+
+ if (!idx1->IsAdd() && !idx1->IsSub()) {
+ // We currently only support Add and Sub operations.
+ return true;
+ }
+
+ HConstant* cst = idx1->GetConstantRight();
+ if (cst == nullptr || cst->IsArithmeticZero()) {
+ return true;
+ }
+
+ if (idx1->GetLeastConstantLeft() == idx2) {
+ // for example, array[idx1 + 1] and array[idx1]
+ return false;
+ }
+
+ return true;
+}
+
+// Check if Add and Sub MAY alias when used as indices in arrays.
+static bool BinaryOpsMayAlias(const HBinaryOperation* idx1, const HBinaryOperation* idx2) {
+ DCHECK(idx1!= nullptr);
+ DCHECK(idx2 != nullptr);
+
+ HConstant* idx1_cst = idx1->GetConstantRight();
+ HInstruction* idx1_other = idx1->GetLeastConstantLeft();
+ HConstant* idx2_cst = idx2->GetConstantRight();
+ HInstruction* idx2_other = idx2->GetLeastConstantLeft();
+
+ if (idx1_cst == nullptr || idx1_other == nullptr ||
+ idx2_cst == nullptr || idx2_other == nullptr) {
+ // We only analyze patterns like [i +/- CONST].
+ return true;
+ }
+
+ if (idx1_other != idx2_other) {
+ // For example, [j+1] and [k+1] MAY alias.
+ return true;
+ }
+
+ if ((idx1->IsAdd() && idx2->IsAdd()) ||
+ (idx1->IsSub() && idx2->IsSub())) {
+ return idx1_cst->AsIntConstant()->GetValue() == idx2_cst->AsIntConstant()->GetValue();
+ }
+
+ if ((idx1->IsAdd() && idx2->IsSub()) ||
+ (idx1->IsSub() && idx2->IsAdd())) {
+ // [i + CONST1] and [i - CONST2] MAY alias iff CONST1 == -CONST2.
+ // By checking CONST1 == -CONST2, following cases are handled:
+ // - Zero constants case [i+0] and [i-0] is handled.
+ // - Overflow cases are handled, for example:
+ // [i+0x80000000] and [i-0x80000000];
+ // [i+0x10] and [i-0xFFFFFFF0].
+ // - Other cases [i+CONST1] and [i-CONST2] without any overflow is handled.
+ return idx1_cst->AsIntConstant()->GetValue() == -(idx2_cst->AsIntConstant()->GetValue());
+ }
+
+ // All other cases, MAY alias.
+ return true;
+}
+
+// The following array index cases are handled:
+// [i] and [i]
+// [CONST1] and [CONST2]
+// [i] and [i+CONST]
+// [i] and [i-CONST]
+// [i+CONST1] and [i+CONST2]
+// [i-CONST1] and [i-CONST2]
+// [i+CONST1] and [i-CONST2]
+// [i-CONST1] and [i+CONST2]
+// For other complicated cases, we rely on other passes like GVN and simpilfier
+// to optimize these cases before this pass.
+// For example: [i+j+k+10] and [i+k+10+j] shall be optimized to [i7+10] and [i7+10].
+bool HeapLocationCollector::CanArrayIndicesAlias(const HInstruction* idx1,
+ const HInstruction* idx2) const {
+ DCHECK(idx1 != nullptr);
+ DCHECK(idx2 != nullptr);
+
+ if (idx1 == idx2) {
+ // [i] and [i]
+ return true;
+ }
+ if (idx1->IsIntConstant() && idx2->IsIntConstant()) {
+ // [CONST1] and [CONST2]
+ return idx1->AsIntConstant()->GetValue() == idx2->AsIntConstant()->GetValue();
+ }
+
+ if (idx1->IsBinaryOperation() && !BinaryOpAndIndexMayAlias(idx1->AsBinaryOperation(), idx2)) {
+ // [i] and [i+/-CONST]
+ return false;
+ }
+ if (idx2->IsBinaryOperation() && !BinaryOpAndIndexMayAlias(idx2->AsBinaryOperation(), idx1)) {
+ // [i+/-CONST] and [i]
+ return false;
+ }
+
+ if (idx1->IsBinaryOperation() && idx2->IsBinaryOperation()) {
+ // [i+/-CONST1] and [i+/-CONST2]
+ if (!BinaryOpsMayAlias(idx1->AsBinaryOperation(), idx2->AsBinaryOperation())) {
+ return false;
+ }
+ }
+
+ // By default, MAY alias.
+ return true;
+}
+
void LoadStoreAnalysis::Run() {
for (HBasicBlock* block : graph_->GetReversePostOrder()) {
heap_location_collector_.VisitBasicBlock(block);
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 4e940f30bf..86fb8e0165 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -214,6 +214,17 @@ class HeapLocationCollector : public HGraphVisitor {
return nullptr;
}
+ size_t GetArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const {
+ DCHECK(array != nullptr);
+ DCHECK(index != nullptr);
+ HInstruction* original_ref = HuntForOriginalReference(array);
+ ReferenceInfo* ref_info = FindReferenceInfoOf(original_ref);
+ return FindHeapLocationIndex(ref_info,
+ HeapLocation::kInvalidFieldOffset,
+ index,
+ HeapLocation::kDeclaringClassDefIndexForArrays);
+ }
+
bool HasHeapStores() const {
return has_heap_stores_;
}
@@ -300,6 +311,8 @@ class HeapLocationCollector : public HGraphVisitor {
return true;
}
+ bool CanArrayIndicesAlias(const HInstruction* i1, const HInstruction* i2) const;
+
// `index1` and `index2` are indices in the array of collected heap locations.
// Returns the position in the bit vector that tracks whether the two heap
// locations may alias.
@@ -336,12 +349,7 @@ class HeapLocationCollector : public HGraphVisitor {
if (loc1->IsArrayElement() && loc2->IsArrayElement()) {
HInstruction* array_index1 = loc1->GetIndex();
HInstruction* array_index2 = loc2->GetIndex();
- DCHECK(array_index1 != nullptr);
- DCHECK(array_index2 != nullptr);
- if (array_index1->IsIntConstant() &&
- array_index2->IsIntConstant() &&
- array_index1->AsIntConstant()->GetValue() != array_index2->AsIntConstant()->GetValue()) {
- // Different constant indices do not alias.
+ if (!CanArrayIndicesAlias(array_index1, array_index2)) {
return false;
}
ReferenceInfo* ref_info = loc1->GetReferenceInfo();
diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc
index 24187777f6..81344b52f6 100644
--- a/compiler/optimizing/load_store_analysis_test.cc
+++ b/compiler/optimizing/load_store_analysis_test.cc
@@ -184,4 +184,198 @@ TEST_F(LoadStoreAnalysisTest, FieldHeapLocations) {
ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
}
+TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) {
+ HBasicBlock* entry = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry);
+ graph_->SetEntryBlock(entry);
+ graph_->BuildDominatorTree();
+
+ HInstruction* array = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
+ HInstruction* index = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(1), 1, Primitive::kPrimInt);
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* c_neg1 = graph_->GetIntConstant(-1);
+ HInstruction* add0 = new (&allocator_) HAdd(Primitive::kPrimInt, index, c0);
+ HInstruction* add1 = new (&allocator_) HAdd(Primitive::kPrimInt, index, c1);
+ HInstruction* sub0 = new (&allocator_) HSub(Primitive::kPrimInt, index, c0);
+ HInstruction* sub1 = new (&allocator_) HSub(Primitive::kPrimInt, index, c1);
+ HInstruction* sub_neg1 = new (&allocator_) HSub(Primitive::kPrimInt, index, c_neg1);
+ HInstruction* rev_sub1 = new (&allocator_) HSub(Primitive::kPrimInt, c1, index);
+ HInstruction* arr_set1 = new (&allocator_) HArraySet(array, c0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set2 = new (&allocator_) HArraySet(array, c1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set3 = new (&allocator_) HArraySet(array, add0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set4 = new (&allocator_) HArraySet(array, add1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set5 = new (&allocator_) HArraySet(array, sub0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set6 = new (&allocator_) HArraySet(array, sub1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set7 = new (&allocator_) HArraySet(array, rev_sub1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set8 = new (&allocator_) HArraySet(array, sub_neg1, c0, Primitive::kPrimInt, 0);
+
+ entry->AddInstruction(array);
+ entry->AddInstruction(index);
+ entry->AddInstruction(add0);
+ entry->AddInstruction(add1);
+ entry->AddInstruction(sub0);
+ entry->AddInstruction(sub1);
+ entry->AddInstruction(sub_neg1);
+ entry->AddInstruction(rev_sub1);
+
+ entry->AddInstruction(arr_set1); // array[0] = c0
+ entry->AddInstruction(arr_set2); // array[1] = c0
+ entry->AddInstruction(arr_set3); // array[i+0] = c0
+ entry->AddInstruction(arr_set4); // array[i+1] = c0
+ entry->AddInstruction(arr_set5); // array[i-0] = c0
+ entry->AddInstruction(arr_set6); // array[i-1] = c0
+ entry->AddInstruction(arr_set7); // array[1-i] = c0
+ entry->AddInstruction(arr_set8); // array[i-(-1)] = c0
+
+ LoadStoreAnalysis lsa(graph_);
+ lsa.Run();
+ const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector();
+
+ // LSA/HeapLocationCollector should see those ArrayGet instructions.
+ ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 8U);
+ ASSERT_TRUE(heap_location_collector.HasHeapStores());
+
+ // Test queries on HeapLocationCollector's aliasing matrix after load store analysis.
+ size_t loc1 = HeapLocationCollector::kHeapLocationNotFound;
+ size_t loc2 = HeapLocationCollector::kHeapLocationNotFound;
+
+ // Test alias: array[0] and array[1]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, c0);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, c1);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+0] and array[i-0]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add0);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub0);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+1] and array[i-1]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub1);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+1] and array[1-i]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, rev_sub1);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+1] and array[i-(-1)]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_neg1);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+}
+
+TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) {
+ HBasicBlock* entry = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry);
+ graph_->SetEntryBlock(entry);
+ graph_->BuildDominatorTree();
+
+ HInstruction* array = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(0), 0, Primitive::kPrimNot);
+ HInstruction* index = new (&allocator_) HParameterValue(
+ graph_->GetDexFile(), dex::TypeIndex(1), 1, Primitive::kPrimInt);
+
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c_0x80000000 = graph_->GetIntConstant(0x80000000);
+ HInstruction* c_0x10 = graph_->GetIntConstant(0x10);
+ HInstruction* c_0xFFFFFFF0 = graph_->GetIntConstant(0xFFFFFFF0);
+ HInstruction* c_0x7FFFFFFF = graph_->GetIntConstant(0x7FFFFFFF);
+ HInstruction* c_0x80000001 = graph_->GetIntConstant(0x80000001);
+
+ // `index+0x80000000` and `index-0x80000000` array indices MAY alias.
+ HInstruction* add_0x80000000 = new (&allocator_) HAdd(Primitive::kPrimInt, index, c_0x80000000);
+ HInstruction* sub_0x80000000 = new (&allocator_) HSub(Primitive::kPrimInt, index, c_0x80000000);
+ HInstruction* arr_set_1 = new (&allocator_) HArraySet(
+ array, add_0x80000000, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_2 = new (&allocator_) HArraySet(
+ array, sub_0x80000000, c0, Primitive::kPrimInt, 0);
+
+ // `index+0x10` and `index-0xFFFFFFF0` array indices MAY alias.
+ HInstruction* add_0x10 = new (&allocator_) HAdd(Primitive::kPrimInt, index, c_0x10);
+ HInstruction* sub_0xFFFFFFF0 = new (&allocator_) HSub(Primitive::kPrimInt, index, c_0xFFFFFFF0);
+ HInstruction* arr_set_3 = new (&allocator_) HArraySet(
+ array, add_0x10, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_4 = new (&allocator_) HArraySet(
+ array, sub_0xFFFFFFF0, c0, Primitive::kPrimInt, 0);
+
+ // `index+0x7FFFFFFF` and `index-0x80000001` array indices MAY alias.
+ HInstruction* add_0x7FFFFFFF = new (&allocator_) HAdd(Primitive::kPrimInt, index, c_0x7FFFFFFF);
+ HInstruction* sub_0x80000001 = new (&allocator_) HSub(Primitive::kPrimInt, index, c_0x80000001);
+ HInstruction* arr_set_5 = new (&allocator_) HArraySet(
+ array, add_0x7FFFFFFF, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_6 = new (&allocator_) HArraySet(
+ array, sub_0x80000001, c0, Primitive::kPrimInt, 0);
+
+ // `index+0` and `index-0` array indices MAY alias.
+ HInstruction* add_0 = new (&allocator_) HAdd(Primitive::kPrimInt, index, c0);
+ HInstruction* sub_0 = new (&allocator_) HSub(Primitive::kPrimInt, index, c0);
+ HInstruction* arr_set_7 = new (&allocator_) HArraySet(array, add_0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_8 = new (&allocator_) HArraySet(array, sub_0, c0, Primitive::kPrimInt, 0);
+
+ entry->AddInstruction(array);
+ entry->AddInstruction(index);
+ entry->AddInstruction(add_0x80000000);
+ entry->AddInstruction(sub_0x80000000);
+ entry->AddInstruction(add_0x10);
+ entry->AddInstruction(sub_0xFFFFFFF0);
+ entry->AddInstruction(add_0x7FFFFFFF);
+ entry->AddInstruction(sub_0x80000001);
+ entry->AddInstruction(add_0);
+ entry->AddInstruction(sub_0);
+ entry->AddInstruction(arr_set_1);
+ entry->AddInstruction(arr_set_2);
+ entry->AddInstruction(arr_set_3);
+ entry->AddInstruction(arr_set_4);
+ entry->AddInstruction(arr_set_5);
+ entry->AddInstruction(arr_set_6);
+ entry->AddInstruction(arr_set_7);
+ entry->AddInstruction(arr_set_8);
+
+ LoadStoreAnalysis lsa(graph_);
+ lsa.Run();
+ const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector();
+
+ // LSA/HeapLocationCollector should see those ArrayGet instructions.
+ ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 8U);
+ ASSERT_TRUE(heap_location_collector.HasHeapStores());
+
+ // Test queries on HeapLocationCollector's aliasing matrix after load store analysis.
+ size_t loc1 = HeapLocationCollector::kHeapLocationNotFound;
+ size_t loc2 = HeapLocationCollector::kHeapLocationNotFound;
+
+ // Test alias: array[i+0x80000000] and array[i-0x80000000]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x80000000);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+0x10] and array[i-0xFFFFFFF0]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x10);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0xFFFFFFF0);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+0x7FFFFFFF] and array[i-0x80000001]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x7FFFFFFF);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000001);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Test alias: array[i+0] and array[i-0]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Should not alias:
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000001);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+
+ // Should not alias:
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+}
+
} // namespace art
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index d2493137fe..83f31c77d3 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -31,6 +31,9 @@ namespace art {
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
+// All current SIMD targets want 16-byte alignment.
+static constexpr size_t kAlignedBase = 16;
+
// Remove the instruction from the graph. A bit more elaborate than the usual
// instruction removal, since there may be a cycle in the use structure.
static void RemoveFromCycle(HInstruction* instruction) {
@@ -283,6 +286,9 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,
simplified_(false),
vector_length_(0),
vector_refs_(nullptr),
+ vector_peeling_candidate_(nullptr),
+ vector_runtime_test_a_(nullptr),
+ vector_runtime_test_b_(nullptr),
vector_map_(nullptr) {
}
@@ -422,23 +428,6 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
// Optimization.
//
-bool HLoopOptimization::CanRemoveCycle() {
- for (HInstruction* i : *iset_) {
- // We can never remove instructions that have environment
- // uses when we compile 'debuggable'.
- if (i->HasEnvironmentUses() && graph_->IsDebuggable()) {
- return false;
- }
- // A deoptimization should never have an environment input removed.
- for (const HUseListNode<HEnvironment*>& use : i->GetEnvUses()) {
- if (use.GetUser()->GetHolder()->IsDeoptimize()) {
- return false;
- }
- }
- }
- return true;
-}
-
void HLoopOptimization::SimplifyInduction(LoopNode* node) {
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
@@ -565,7 +554,7 @@ void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
if (kEnableVectorization) {
iset_->clear(); // prepare phi induction
if (TrySetSimpleLoopHeader(header) &&
- CanVectorize(node, body, trip_count) &&
+ ShouldVectorize(node, body, trip_count) &&
TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
Vectorize(node, body, exit, trip_count);
graph_->SetHasSIMD(true); // flag SIMD usage
@@ -580,10 +569,11 @@ void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
// Intel Press, June, 2004 (http://www.aartbik.com/).
//
-bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
+bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
// Reset vector bookkeeping.
vector_length_ = 0;
vector_refs_->clear();
+ vector_peeling_candidate_ = nullptr;
vector_runtime_test_a_ =
vector_runtime_test_b_= nullptr;
@@ -600,12 +590,9 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
}
}
- // Heuristics. Does vectorization seem profitable?
- // TODO: refine
- if (vector_length_ == 0) {
- return false; // nothing found
- } else if (0 < trip_count && trip_count < vector_length_) {
- return false; // insufficient iterations
+ // Does vectorization seem profitable?
+ if (!IsVectorizationProfitable(trip_count)) {
+ return false;
}
// Data dependence analysis. Find each pair of references with same type, where
@@ -633,18 +620,24 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
// Conservatively assume a potential loop-carried data dependence otherwise, avoided by
// generating an explicit a != b disambiguation runtime test on the two references.
if (x != y) {
- // For now, we reject after one test to avoid excessive overhead.
- if (vector_runtime_test_a_ != nullptr) {
- return false;
+ // To avoid excessive overhead, we only accept one a != b test.
+ if (vector_runtime_test_a_ == nullptr) {
+ // First test found.
+ vector_runtime_test_a_ = a;
+ vector_runtime_test_b_ = b;
+ } else if ((vector_runtime_test_a_ != a || vector_runtime_test_b_ != b) &&
+ (vector_runtime_test_a_ != b || vector_runtime_test_b_ != a)) {
+ return false; // second test would be needed
}
- vector_runtime_test_a_ = a;
- vector_runtime_test_b_ = b;
}
}
}
}
}
+ // Consider dynamic loop peeling for alignment.
+ SetPeelingCandidate(trip_count);
+
// Success!
return true;
}
@@ -657,28 +650,52 @@ void HLoopOptimization::Vectorize(LoopNode* node,
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
- // A cleanup is needed for any unknown trip count or for a known trip count
- // with remainder iterations after vectorization.
- bool needs_cleanup = trip_count == 0 || (trip_count % vector_length_) != 0;
+ // Pick a loop unrolling factor for the vector loop.
+ uint32_t unroll = GetUnrollingFactor(block, trip_count);
+ uint32_t chunk = vector_length_ * unroll;
+
+ // A cleanup loop is needed, at least, for any unknown trip count or
+ // for a known trip count with remainder iterations after vectorization.
+ bool needs_cleanup = trip_count == 0 || (trip_count % chunk) != 0;
// Adjust vector bookkeeping.
iset_->clear(); // prepare phi induction
bool is_simple_loop_header = TrySetSimpleLoopHeader(header); // fills iset_
DCHECK(is_simple_loop_header);
+ vector_header_ = header;
+ vector_body_ = block;
+
+ // Generate dynamic loop peeling trip count, if needed:
+ // ptc = <peeling-needed-for-candidate>
+ HInstruction* ptc = nullptr;
+ if (vector_peeling_candidate_ != nullptr) {
+ DCHECK_LT(vector_length_, trip_count) << "dynamic peeling currently requires known trip count";
+ //
+ // TODO: Implement this. Compute address of first access memory location and
+ // compute peeling factor to obtain kAlignedBase alignment.
+ //
+ needs_cleanup = true;
+ }
- // Generate preheader:
+ // Generate loop control:
// stc = <trip-count>;
- // vtc = stc - stc % VL;
+ // vtc = stc - (stc - ptc) % chunk;
+ // i = 0;
HInstruction* stc = induction_range_.GenerateTripCount(node->loop_info, graph_, preheader);
HInstruction* vtc = stc;
if (needs_cleanup) {
- DCHECK(IsPowerOfTwo(vector_length_));
+ DCHECK(IsPowerOfTwo(chunk));
+ HInstruction* diff = stc;
+ if (ptc != nullptr) {
+ diff = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, ptc));
+ }
HInstruction* rem = Insert(
preheader, new (global_allocator_) HAnd(induc_type,
- stc,
- graph_->GetIntConstant(vector_length_ - 1)));
+ diff,
+ graph_->GetIntConstant(chunk - 1)));
vtc = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, rem));
}
+ vector_index_ = graph_->GetIntConstant(0);
// Generate runtime disambiguation test:
// vtc = a != b ? vtc : 0;
@@ -691,16 +708,31 @@ void HLoopOptimization::Vectorize(LoopNode* node,
needs_cleanup = true;
}
- // Generate vector loop:
- // for (i = 0; i < vtc; i += VL)
+ // Generate dynamic peeling loop for alignment, if needed:
+ // for ( ; i < ptc; i += 1)
+ // <loop-body>
+ if (ptc != nullptr) {
+ vector_mode_ = kSequential;
+ GenerateNewLoop(node,
+ block,
+ graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+ vector_index_,
+ ptc,
+ graph_->GetIntConstant(1),
+ /*unroll*/ 1);
+ }
+
+ // Generate vector loop, possibly further unrolled:
+ // for ( ; i < vtc; i += chunk)
// <vectorized-loop-body>
vector_mode_ = kVector;
GenerateNewLoop(node,
block,
- graph_->TransformLoopForVectorization(header, block, exit),
- graph_->GetIntConstant(0),
+ graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+ vector_index_,
vtc,
- graph_->GetIntConstant(vector_length_));
+ graph_->GetIntConstant(vector_length_), // increment per unroll
+ unroll);
HLoopInformation* vloop = vector_header_->GetLoopInformation();
// Generate cleanup loop, if needed:
@@ -711,9 +743,10 @@ void HLoopOptimization::Vectorize(LoopNode* node,
GenerateNewLoop(node,
block,
graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
- vector_phi_,
+ vector_index_,
stc,
- graph_->GetIntConstant(1));
+ graph_->GetIntConstant(1),
+ /*unroll*/ 1);
}
// Remove the original loop by disconnecting the body block
@@ -722,8 +755,9 @@ void HLoopOptimization::Vectorize(LoopNode* node,
while (!header->GetFirstInstruction()->IsGoto()) {
header->RemoveInstruction(header->GetFirstInstruction());
}
- // Update loop hierarchy: the old header now resides in the
- // same outer loop as the old preheader.
+ // Update loop hierarchy: the old header now resides in the same outer loop
+ // as the old preheader. Note that we don't bother putting sequential
+ // loops back in the hierarchy at this point.
header->SetLoopInformation(preheader->GetLoopInformation()); // outward
node->loop_info = vloop;
}
@@ -733,44 +767,64 @@ void HLoopOptimization::GenerateNewLoop(LoopNode* node,
HBasicBlock* new_preheader,
HInstruction* lo,
HInstruction* hi,
- HInstruction* step) {
+ HInstruction* step,
+ uint32_t unroll) {
+ DCHECK(unroll == 1 || vector_mode_ == kVector);
Primitive::Type induc_type = Primitive::kPrimInt;
// Prepare new loop.
- vector_map_->clear();
vector_preheader_ = new_preheader,
vector_header_ = vector_preheader_->GetSingleSuccessor();
vector_body_ = vector_header_->GetSuccessors()[1];
- vector_phi_ = new (global_allocator_) HPhi(global_allocator_,
- kNoRegNumber,
- 0,
- HPhi::ToPhiType(induc_type));
+ HPhi* phi = new (global_allocator_) HPhi(global_allocator_,
+ kNoRegNumber,
+ 0,
+ HPhi::ToPhiType(induc_type));
// Generate header and prepare body.
// for (i = lo; i < hi; i += step)
// <loop-body>
- HInstruction* cond = new (global_allocator_) HAboveOrEqual(vector_phi_, hi);
- vector_header_->AddPhi(vector_phi_);
+ HInstruction* cond = new (global_allocator_) HAboveOrEqual(phi, hi);
+ vector_header_->AddPhi(phi);
vector_header_->AddInstruction(cond);
vector_header_->AddInstruction(new (global_allocator_) HIf(cond));
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
- DCHECK(vectorized_def);
- }
- // Generate body from the instruction map, but in original program order.
- HEnvironment* env = vector_header_->GetFirstInstruction()->GetEnvironment();
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- auto i = vector_map_->find(it.Current());
- if (i != vector_map_->end() && !i->second->IsInBlock()) {
- Insert(vector_body_, i->second);
- // Deal with instructions that need an environment, such as the scalar intrinsics.
- if (i->second->NeedsEnvironment()) {
- i->second->CopyEnvironmentFromWithLoopPhiAdjustment(env, vector_header_);
+ vector_index_ = phi;
+ for (uint32_t u = 0; u < unroll; u++) {
+ // Clear map, leaving loop invariants setup during unrolling.
+ if (u == 0) {
+ vector_map_->clear();
+ } else {
+ for (auto i = vector_map_->begin(); i != vector_map_->end(); ) {
+ if (i->second->IsVecReplicateScalar()) {
+ DCHECK(node->loop_info->IsDefinedOutOfTheLoop(i->first));
+ ++i;
+ } else {
+ i = vector_map_->erase(i);
+ }
}
}
+ // Generate instruction map.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
+ DCHECK(vectorized_def);
+ }
+ // Generate body from the instruction map, but in original program order.
+ HEnvironment* env = vector_header_->GetFirstInstruction()->GetEnvironment();
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ auto i = vector_map_->find(it.Current());
+ if (i != vector_map_->end() && !i->second->IsInBlock()) {
+ Insert(vector_body_, i->second);
+ // Deal with instructions that need an environment, such as the scalar intrinsics.
+ if (i->second->NeedsEnvironment()) {
+ i->second->CopyEnvironmentFromWithLoopPhiAdjustment(env, vector_header_);
+ }
+ }
+ }
+ vector_index_ = new (global_allocator_) HAdd(induc_type, vector_index_, step);
+ Insert(vector_body_, vector_index_);
}
- // Finalize increment and phi.
- HInstruction* inc = new (global_allocator_) HAdd(induc_type, vector_phi_, step);
- vector_phi_->AddInput(lo);
- vector_phi_->AddInput(Insert(vector_body_, inc));
+ // Finalize phi for the loop index.
+ phi->AddInput(lo);
+ phi->AddInput(vector_index_);
+ vector_index_ = phi;
}
// TODO: accept reductions at left-hand-side, mixed-type store idioms, etc.
@@ -791,11 +845,11 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node,
HInstruction* offset = nullptr;
if (TrySetVectorType(type, &restrictions) &&
node->loop_info->IsDefinedOutOfTheLoop(base) &&
- induction_range_.IsUnitStride(instruction, index, &offset) &&
+ induction_range_.IsUnitStride(instruction, index, graph_, &offset) &&
VectorizeUse(node, value, generate_code, type, restrictions)) {
if (generate_code) {
GenerateVecSub(index, offset);
- GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), type);
+ GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), offset, type);
} else {
vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ true));
}
@@ -849,10 +903,10 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
HInstruction* offset = nullptr;
if (type == instruction->GetType() &&
node->loop_info->IsDefinedOutOfTheLoop(base) &&
- induction_range_.IsUnitStride(instruction, index, &offset)) {
+ induction_range_.IsUnitStride(instruction, index, graph_, &offset)) {
if (generate_code) {
GenerateVecSub(index, offset);
- GenerateVecMem(instruction, vector_map_->Get(index), nullptr, type);
+ GenerateVecMem(instruction, vector_map_->Get(index), nullptr, offset, type);
} else {
vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ false));
}
@@ -1043,6 +1097,23 @@ bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restric
switch (compiler_driver_->GetInstructionSet()) {
case kArm:
case kThumb2:
+ // Allow vectorization for all ARM devices, because Android assumes that
+ // ARM 32-bit always supports advanced SIMD.
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ *restrictions |= kNoDiv;
+ return TrySetVectorLength(8);
+ case Primitive::kPrimChar:
+ case Primitive::kPrimShort:
+ *restrictions |= kNoDiv | kNoStringCharAt;
+ return TrySetVectorLength(4);
+ case Primitive::kPrimInt:
+ *restrictions |= kNoDiv;
+ return TrySetVectorLength(2);
+ default:
+ break;
+ }
return false;
case kArm64:
// Allow vectorization for all ARM devices, because Android assumes that
@@ -1164,8 +1235,9 @@ void HLoopOptimization::GenerateVecInv(HInstruction* org, Primitive::Type type)
void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset) {
if (vector_map_->find(org) == vector_map_->end()) {
- HInstruction* subscript = vector_phi_;
- if (offset != nullptr) {
+ HInstruction* subscript = vector_index_;
+ int64_t value = 0;
+ if (!IsInt64AndGet(offset, &value) || value != 0) {
subscript = new (global_allocator_) HAdd(Primitive::kPrimInt, subscript, offset);
if (org->IsPhi()) {
Insert(vector_body_, subscript); // lacks layout placeholder
@@ -1178,17 +1250,27 @@ void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset)
void HLoopOptimization::GenerateVecMem(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
+ HInstruction* offset,
Primitive::Type type) {
HInstruction* vector = nullptr;
if (vector_mode_ == kVector) {
// Vector store or load.
+ HInstruction* base = org->InputAt(0);
if (opb != nullptr) {
vector = new (global_allocator_) HVecStore(
- global_allocator_, org->InputAt(0), opa, opb, type, vector_length_);
+ global_allocator_, base, opa, opb, type, vector_length_);
} else {
bool is_string_char_at = org->AsArrayGet()->IsStringCharAt();
vector = new (global_allocator_) HVecLoad(
- global_allocator_, org->InputAt(0), opa, type, vector_length_, is_string_char_at);
+ global_allocator_, base, opa, type, vector_length_, is_string_char_at);
+ }
+ // Known dynamically enforced alignment?
+ // TODO: detect offset + constant differences.
+ // TODO: long run, static alignment analysis?
+ if (vector_peeling_candidate_ != nullptr &&
+ vector_peeling_candidate_->base == base &&
+ vector_peeling_candidate_->offset == offset) {
+ vector->AsVecMemoryOperation()->SetAlignment(Alignment(kAlignedBase, 0));
}
} else {
// Scalar store or load.
@@ -1444,10 +1526,57 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
}
//
+// Vectorization heuristics.
+//
+
+bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) {
+ // Current heuristic: non-empty body with sufficient number
+ // of iterations (if known).
+ // TODO: refine by looking at e.g. operation count, alignment, etc.
+ if (vector_length_ == 0) {
+ return false; // nothing found
+ } else if (0 < trip_count && trip_count < vector_length_) {
+ return false; // insufficient iterations
+ }
+ return true;
+}
+
+void HLoopOptimization::SetPeelingCandidate(int64_t trip_count ATTRIBUTE_UNUSED) {
+ // Current heuristic: none.
+ // TODO: implement
+}
+
+uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) {
+ // Current heuristic: unroll by 2 on ARM64/X86 for large known trip
+ // counts and small loop bodies.
+ // TODO: refine with operation count, remaining iterations, etc.
+ // Artem had some really cool ideas for this already.
+ switch (compiler_driver_->GetInstructionSet()) {
+ case kArm64:
+ case kX86:
+ case kX86_64: {
+ size_t num_instructions = block->GetInstructions().CountSize();
+ if (num_instructions <= 10 && trip_count >= 4 * vector_length_) {
+ return 2;
+ }
+ return 1;
+ }
+ default:
+ return 1;
+ }
+}
+
+//
// Helpers.
//
bool HLoopOptimization::TrySetPhiInduction(HPhi* phi, bool restrict_uses) {
+ // Special case Phis that have equivalent in a debuggable setup. Our graph checker isn't
+ // smart enough to follow strongly connected components (and it's probably not worth
+ // it to make it so). See b/33775412.
+ if (graph_->IsDebuggable() && phi->HasEquivalentPhi()) {
+ return false;
+ }
DCHECK(iset_->empty());
ArenaSet<HInstruction*>* set = induction_range_.LookupCycle(phi);
if (set != nullptr) {
@@ -1576,8 +1705,8 @@ bool HLoopOptimization::TryReplaceWithLastValue(HLoopInformation* loop_info,
size_t index = it->GetIndex();
++it; // increment before replacing
if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded?
- HLoopInformation* other_loop_info = user->GetHolder()->GetBlock()->GetLoopInformation();
// Only update environment uses after the loop.
+ HLoopInformation* other_loop_info = user->GetHolder()->GetBlock()->GetLoopInformation();
if (other_loop_info == nullptr || !other_loop_info->IsIn(*loop_info)) {
user->RemoveAsUserOfInput(index);
user->SetRawEnvAt(index, replacement);
@@ -1614,4 +1743,21 @@ void HLoopOptimization::RemoveDeadInstructions(const HInstructionList& list) {
}
}
+bool HLoopOptimization::CanRemoveCycle() {
+ for (HInstruction* i : *iset_) {
+ // We can never remove instructions that have environment
+ // uses when we compile 'debuggable'.
+ if (i->HasEnvironmentUses() && graph_->IsDebuggable()) {
+ return false;
+ }
+ // A deoptimization should never have an environment input removed.
+ for (const HUseListNode<HEnvironment*>& use : i->GetEnvUses()) {
+ if (use.GetUser()->GetHolder()->IsDeoptimize()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index cc6343aeb5..de4bd85fc8 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -116,14 +116,15 @@ class HLoopOptimization : public HOptimization {
void OptimizeInnerLoop(LoopNode* node);
// Vectorization analysis and synthesis.
- bool CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
+ bool ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
void Vectorize(LoopNode* node, HBasicBlock* block, HBasicBlock* exit, int64_t trip_count);
void GenerateNewLoop(LoopNode* node,
HBasicBlock* block,
HBasicBlock* new_preheader,
HInstruction* lo,
HInstruction* hi,
- HInstruction* step);
+ HInstruction* step,
+ uint32_t unroll);
bool VectorizeDef(LoopNode* node, HInstruction* instruction, bool generate_code);
bool VectorizeUse(LoopNode* node,
HInstruction* instruction,
@@ -133,10 +134,11 @@ class HLoopOptimization : public HOptimization {
bool TrySetVectorType(Primitive::Type type, /*out*/ uint64_t* restrictions);
bool TrySetVectorLength(uint32_t length);
void GenerateVecInv(HInstruction* org, Primitive::Type type);
- void GenerateVecSub(HInstruction* org, HInstruction* off);
+ void GenerateVecSub(HInstruction* org, HInstruction* offset);
void GenerateVecMem(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
+ HInstruction* offset,
Primitive::Type type);
void GenerateVecOp(HInstruction* org,
HInstruction* opa,
@@ -151,6 +153,11 @@ class HLoopOptimization : public HOptimization {
Primitive::Type type,
uint64_t restrictions);
+ // Vectorization heuristics.
+ bool IsVectorizationProfitable(int64_t trip_count);
+ void SetPeelingCandidate(int64_t trip_count);
+ uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);
+
// Helpers.
bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
bool TrySetSimpleLoopHeader(HBasicBlock* block);
@@ -208,20 +215,25 @@ class HLoopOptimization : public HOptimization {
// Contents reside in phase-local heap memory.
ArenaSet<ArrayReference>* vector_refs_;
+ // Dynamic loop peeling candidate for alignment.
+ const ArrayReference* vector_peeling_candidate_;
+
+ // Dynamic data dependence test of the form a != b.
+ HInstruction* vector_runtime_test_a_;
+ HInstruction* vector_runtime_test_b_;
+
// Mapping used during vectorization synthesis for both the scalar peeling/cleanup
- // loop (simd_ is false) and the actual vector loop (simd_ is true). The data
+ // loop (mode is kSequential) and the actual vector loop (mode is kVector). The data
// structure maps original instructions into the new instructions.
// Contents reside in phase-local heap memory.
ArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
// Temporary vectorization bookkeeping.
+ VectorMode vector_mode_; // synthesis mode
HBasicBlock* vector_preheader_; // preheader of the new loop
HBasicBlock* vector_header_; // header of the new loop
HBasicBlock* vector_body_; // body of the new loop
- HInstruction* vector_runtime_test_a_;
- HInstruction* vector_runtime_test_b_; // defines a != b runtime test
- HPhi* vector_phi_; // the Phi representing the normalized loop index
- VectorMode vector_mode_; // selects synthesis mode
+ HInstruction* vector_index_; // normalized index of the new loop
friend class LoopOptimizationTest;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ffa16dd787..5e072cdb67 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -421,7 +421,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
void SimplifyLoop(HBasicBlock* header);
int32_t GetNextInstructionId() {
- DCHECK_NE(current_instruction_id_, INT32_MAX);
+ CHECK_NE(current_instruction_id_, INT32_MAX);
return current_instruction_id_++;
}
@@ -430,7 +430,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
}
void SetCurrentInstructionId(int32_t id) {
- DCHECK_GE(id, current_instruction_id_);
+ CHECK_GE(id, current_instruction_id_);
current_instruction_id_ = id;
}
@@ -2612,6 +2612,16 @@ class HPhi FINAL : public HVariableInputSizeInstruction {
&& other->AsPhi()->GetRegNumber() == GetRegNumber();
}
+ bool HasEquivalentPhi() const {
+ if (GetPrevious() != nullptr && GetPrevious()->AsPhi()->GetRegNumber() == GetRegNumber()) {
+ return true;
+ }
+ if (GetNext() != nullptr && GetNext()->AsPhi()->GetRegNumber() == GetRegNumber()) {
+ return true;
+ }
+ return false;
+ }
+
// Returns the next equivalent phi (starting from the current one) or null if there is none.
// An equivalent phi is a phi having the same dex register and type.
// It assumes that phis with the same dex register are adjacent.
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 5dbe29b4fa..dc522a463e 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -46,6 +46,10 @@ class Alignment {
return "ALIGN(" + std::to_string(base_) + "," + std::to_string(offset_) + ")";
}
+ bool operator==(const Alignment& other) const {
+ return base_ == other.base_ && offset_ == other.offset_;
+ }
+
private:
size_t base_;
size_t offset_;
@@ -96,6 +100,13 @@ class HVecOperation : public HVariableInputSizeInstruction {
return GetPackedField<TypeField>();
}
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecOperation* o = other->AsVecOperation();
+ return GetVectorLength() == o->GetVectorLength() && GetPackedType() == o->GetPackedType();
+ }
+
DECLARE_ABSTRACT_INSTRUCTION(VecOperation);
protected:
@@ -189,6 +200,11 @@ class HVecMemoryOperation : public HVecOperation {
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecMemoryOperation* o = other->AsVecMemoryOperation();
+ return HVecOperation::InstructionDataEquals(o) && GetAlignment() == o->GetAlignment();
+ }
+
DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
private:
@@ -378,6 +394,13 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation {
bool IsUnsigned() const { return GetPackedFlag<kFieldHAddIsUnsigned>(); }
bool IsRounded() const { return GetPackedFlag<kFieldHAddIsRounded>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecHalvingAdd* o = other->AsVecHalvingAdd();
+ return HVecOperation::InstructionDataEquals(o) &&
+ IsUnsigned() == o->IsUnsigned() &&
+ IsRounded() == o->IsRounded();
+ }
+
DECLARE_INSTRUCTION(VecHalvingAdd);
private:
@@ -466,6 +489,11 @@ class HVecMin FINAL : public HVecBinaryOperation {
bool IsUnsigned() const { return GetPackedFlag<kFieldMinOpIsUnsigned>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecMin* o = other->AsVecMin();
+ return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
+ }
+
DECLARE_INSTRUCTION(VecMin);
private:
@@ -496,6 +524,11 @@ class HVecMax FINAL : public HVecBinaryOperation {
bool IsUnsigned() const { return GetPackedFlag<kFieldMaxOpIsUnsigned>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecMax* o = other->AsVecMax();
+ return HVecOperation::InstructionDataEquals(o) && IsUnsigned() == o->IsUnsigned();
+ }
+
DECLARE_INSTRUCTION(VecMax);
private:
@@ -694,10 +727,9 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation {
static constexpr int kInputMulLeftIndex = 1;
static constexpr int kInputMulRightIndex = 2;
- bool CanBeMoved() const OVERRIDE { return true; }
-
bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- return op_kind_ == other->AsVecMultiplyAccumulate()->op_kind_;
+ const HVecMultiplyAccumulate* o = other->AsVecMultiplyAccumulate();
+ return HVecOperation::InstructionDataEquals(o) && GetOpKind() == o->GetOpKind();
}
InstructionKind GetOpKind() const { return op_kind_; }
@@ -732,10 +764,16 @@ class HVecLoad FINAL : public HVecMemoryOperation {
SetRawInputAt(1, index);
SetPackedFlag<kFieldIsStringCharAt>(is_string_char_at);
}
- DECLARE_INSTRUCTION(VecLoad);
bool IsStringCharAt() const { return GetPackedFlag<kFieldIsStringCharAt>(); }
+ bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+ const HVecLoad* o = other->AsVecLoad();
+ return HVecMemoryOperation::InstructionDataEquals(o) && IsStringCharAt() == o->IsStringCharAt();
+ }
+
+ DECLARE_INSTRUCTION(VecLoad);
+
private:
// Additional packed bits.
static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
@@ -767,7 +805,11 @@ class HVecStore FINAL : public HVecMemoryOperation {
SetRawInputAt(1, index);
SetRawInputAt(2, value);
}
+
+ bool CanBeMoved() const OVERRIDE { return false; }
+
DECLARE_INSTRUCTION(VecStore);
+
private:
DISALLOW_COPY_AND_ASSIGN(HVecStore);
};
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
new file mode 100644
index 0000000000..d33f8e5d2e
--- /dev/null
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/arena_allocator.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+namespace art {
+
+/**
+ * Fixture class for testing vector nodes.
+ */
+class NodesVectorTest : public CommonCompilerTest {
+ public:
+ NodesVectorTest()
+ : pool_(),
+ allocator_(&pool_),
+ graph_(CreateGraph(&allocator_)) {
+ BuildGraph();
+ }
+
+ ~NodesVectorTest() { }
+
+ void BuildGraph() {
+ graph_->SetNumberOfVRegs(1);
+ entry_block_ = new (&allocator_) HBasicBlock(graph_);
+ exit_block_ = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry_block_);
+ graph_->AddBlock(exit_block_);
+ graph_->SetEntryBlock(entry_block_);
+ graph_->SetExitBlock(exit_block_);
+ parameter_ = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ Primitive::kPrimInt);
+ entry_block_->AddInstruction(parameter_);
+ }
+
+ // General building fields.
+ ArenaPool pool_;
+ ArenaAllocator allocator_;
+ HGraph* graph_;
+
+ HBasicBlock* entry_block_;
+ HBasicBlock* exit_block_;
+
+ HInstruction* parameter_;
+};
+
+//
+// The actual vector nodes tests.
+//
+
+TEST(NodesVector, Alignment) {
+ EXPECT_TRUE(Alignment(1, 0).IsAlignedAt(1));
+ EXPECT_FALSE(Alignment(1, 0).IsAlignedAt(2));
+
+ EXPECT_TRUE(Alignment(2, 0).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(2, 1).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(2, 0).IsAlignedAt(2));
+ EXPECT_FALSE(Alignment(2, 1).IsAlignedAt(2));
+ EXPECT_FALSE(Alignment(2, 0).IsAlignedAt(4));
+ EXPECT_FALSE(Alignment(2, 1).IsAlignedAt(4));
+
+ EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(4, 2).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(2));
+ EXPECT_TRUE(Alignment(4, 2).IsAlignedAt(2));
+ EXPECT_TRUE(Alignment(4, 0).IsAlignedAt(4));
+ EXPECT_FALSE(Alignment(4, 2).IsAlignedAt(4));
+ EXPECT_FALSE(Alignment(4, 0).IsAlignedAt(8));
+ EXPECT_FALSE(Alignment(4, 2).IsAlignedAt(8));
+
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(1));
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(2));
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(4));
+ EXPECT_TRUE(Alignment(16, 8).IsAlignedAt(8));
+ EXPECT_TRUE(Alignment(16, 0).IsAlignedAt(16));
+ EXPECT_FALSE(Alignment(16, 1).IsAlignedAt(16));
+ EXPECT_FALSE(Alignment(16, 7).IsAlignedAt(16));
+ EXPECT_FALSE(Alignment(16, 0).IsAlignedAt(32));
+}
+
+TEST(NodesVector, AlignmentEQ) {
+ EXPECT_TRUE(Alignment(2, 0) == Alignment(2, 0));
+ EXPECT_TRUE(Alignment(2, 1) == Alignment(2, 1));
+ EXPECT_TRUE(Alignment(4, 0) == Alignment(4, 0));
+ EXPECT_TRUE(Alignment(4, 2) == Alignment(4, 2));
+
+ EXPECT_FALSE(Alignment(4, 0) == Alignment(2, 0));
+ EXPECT_FALSE(Alignment(4, 0) == Alignment(4, 1));
+ EXPECT_FALSE(Alignment(4, 0) == Alignment(8, 0));
+}
+
+TEST(NodesVector, AlignmentString) {
+ EXPECT_STREQ("ALIGN(1,0)", Alignment(1, 0).ToString().c_str());
+
+ EXPECT_STREQ("ALIGN(2,0)", Alignment(2, 0).ToString().c_str());
+ EXPECT_STREQ("ALIGN(2,1)", Alignment(2, 1).ToString().c_str());
+
+ EXPECT_STREQ("ALIGN(16,0)", Alignment(16, 0).ToString().c_str());
+ EXPECT_STREQ("ALIGN(16,1)", Alignment(16, 1).ToString().c_str());
+ EXPECT_STREQ("ALIGN(16,8)", Alignment(16, 8).ToString().c_str());
+ EXPECT_STREQ("ALIGN(16,9)", Alignment(16, 9).ToString().c_str());
+}
+
+TEST_F(NodesVectorTest, VectorOperationProperties) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+ HVecOperation* v1 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+ HVecOperation* v2 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 2);
+ HVecOperation* v3 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimShort, 4);
+ HVecOperation* v4 = new (&allocator_)
+ HVecStore(&allocator_, parameter_, parameter_, v0, Primitive::kPrimInt, 4);
+
+ EXPECT_TRUE(v0->Equals(v0));
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+ EXPECT_TRUE(v4->Equals(v4));
+
+ EXPECT_TRUE(v0->Equals(v1));
+ EXPECT_FALSE(v0->Equals(v2)); // different vector lengths
+ EXPECT_FALSE(v0->Equals(v3)); // different packed types
+ EXPECT_FALSE(v0->Equals(v4)); // different kinds
+
+ EXPECT_TRUE(v1->Equals(v0)); // switch operands
+ EXPECT_FALSE(v4->Equals(v0));
+
+ EXPECT_EQ(4u, v0->GetVectorLength());
+ EXPECT_EQ(4u, v1->GetVectorLength());
+ EXPECT_EQ(2u, v2->GetVectorLength());
+ EXPECT_EQ(4u, v3->GetVectorLength());
+ EXPECT_EQ(4u, v4->GetVectorLength());
+
+ EXPECT_EQ(Primitive::kPrimDouble, v0->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v1->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v2->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v3->GetType());
+ EXPECT_EQ(Primitive::kPrimDouble, v4->GetType());
+
+ EXPECT_EQ(Primitive::kPrimInt, v0->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimInt, v1->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimInt, v2->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimShort, v3->GetPackedType());
+ EXPECT_EQ(Primitive::kPrimInt, v4->GetPackedType());
+
+ EXPECT_EQ(16u, v0->GetVectorNumberOfBytes());
+ EXPECT_EQ(16u, v1->GetVectorNumberOfBytes());
+ EXPECT_EQ(8u, v2->GetVectorNumberOfBytes());
+ EXPECT_EQ(8u, v3->GetVectorNumberOfBytes());
+ EXPECT_EQ(16u, v4->GetVectorNumberOfBytes());
+
+ EXPECT_TRUE(v0->CanBeMoved());
+ EXPECT_TRUE(v1->CanBeMoved());
+ EXPECT_TRUE(v2->CanBeMoved());
+ EXPECT_TRUE(v3->CanBeMoved());
+ EXPECT_FALSE(v4->CanBeMoved());
+}
+
+TEST_F(NodesVectorTest, VectorAlignmentAndStringCharAtMatterOnLoad) {
+ HVecLoad* v0 = new (&allocator_)
+ HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ false);
+ HVecLoad* v1 = new (&allocator_)
+ HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ false);
+ HVecLoad* v2 = new (&allocator_)
+ HVecLoad(&allocator_, parameter_, parameter_, Primitive::kPrimInt, 4, /*is_string_char_at*/ true);
+
+ EXPECT_FALSE(v0->IsStringCharAt());
+ EXPECT_FALSE(v1->IsStringCharAt());
+ EXPECT_TRUE(v2->IsStringCharAt());
+
+ EXPECT_TRUE(v0->Equals(v0));
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+
+ EXPECT_TRUE(v0->Equals(v1));
+ EXPECT_FALSE(v0->Equals(v2));
+
+ EXPECT_TRUE(v0->GetAlignment() == Alignment(4, 0));
+ EXPECT_TRUE(v1->GetAlignment() == Alignment(4, 0));
+ EXPECT_TRUE(v2->GetAlignment() == Alignment(4, 0));
+
+ v1->SetAlignment(Alignment(8, 0));
+
+ EXPECT_TRUE(v1->GetAlignment() == Alignment(8, 0));
+
+ EXPECT_FALSE(v0->Equals(v1)); // no longer equal
+}
+
+TEST_F(NodesVectorTest, VectorSignMattersOnMin) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecMin* v1 = new (&allocator_)
+ HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true);
+ HVecMin* v2 = new (&allocator_)
+ HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false);
+ HVecMin* v3 = new (&allocator_)
+ HVecMin(&allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true);
+
+ EXPECT_TRUE(v1->IsUnsigned());
+ EXPECT_FALSE(v2->IsUnsigned());
+ EXPECT_TRUE(v3->IsUnsigned());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different signs
+ EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorSignMattersOnMax) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecMax* v1 = new (&allocator_)
+ HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true);
+ HVecMax* v2 = new (&allocator_)
+ HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false);
+ HVecMax* v3 = new (&allocator_)
+ HVecMax(&allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true);
+
+ EXPECT_TRUE(v1->IsUnsigned());
+ EXPECT_FALSE(v2->IsUnsigned());
+ EXPECT_TRUE(v3->IsUnsigned());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different signs
+ EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorAttributesMatterOnHalvingAdd) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecHalvingAdd* v1 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true, /*is_rounded*/ true);
+ HVecHalvingAdd* v2 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ true, /*is_rounded*/ false);
+ HVecHalvingAdd* v3 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false, /*is_rounded*/ true);
+ HVecHalvingAdd* v4 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 4, /*is_unsigned*/ false, /*is_rounded*/ false);
+ HVecHalvingAdd* v5 = new (&allocator_) HVecHalvingAdd(
+ &allocator_, v0, v0, Primitive::kPrimInt, 2, /*is_unsigned*/ true, /*is_rounded*/ true);
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+ EXPECT_TRUE(v4->Equals(v4));
+ EXPECT_TRUE(v5->Equals(v5));
+
+ EXPECT_TRUE(v1->IsUnsigned() && v1->IsRounded());
+ EXPECT_TRUE(v2->IsUnsigned() && !v2->IsRounded());
+ EXPECT_TRUE(!v3->IsUnsigned() && v3->IsRounded());
+ EXPECT_TRUE(!v4->IsUnsigned() && !v4->IsRounded());
+ EXPECT_TRUE(v5->IsUnsigned() && v5->IsRounded());
+
+ EXPECT_FALSE(v1->Equals(v2)); // different attributes
+ EXPECT_FALSE(v1->Equals(v3)); // different attributes
+ EXPECT_FALSE(v1->Equals(v4)); // different attributes
+ EXPECT_FALSE(v1->Equals(v5)); // different vector lengths
+}
+
+TEST_F(NodesVectorTest, VectorOperationMattersOnMultiplyAccumulate) {
+ HVecOperation* v0 = new (&allocator_)
+ HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+ HVecMultiplyAccumulate* v1 = new (&allocator_)
+ HVecMultiplyAccumulate(&allocator_, HInstruction::kAdd, v0, v0, v0, Primitive::kPrimInt, 4);
+ HVecMultiplyAccumulate* v2 = new (&allocator_)
+ HVecMultiplyAccumulate(&allocator_, HInstruction::kSub, v0, v0, v0, Primitive::kPrimInt, 4);
+ HVecMultiplyAccumulate* v3 = new (&allocator_)
+ HVecMultiplyAccumulate(&allocator_, HInstruction::kAdd, v0, v0, v0, Primitive::kPrimInt, 2);
+
+ EXPECT_EQ(HInstruction::kAdd, v1->GetOpKind());
+ EXPECT_EQ(HInstruction::kSub, v2->GetOpKind());
+ EXPECT_EQ(HInstruction::kAdd, v3->GetOpKind());
+
+ EXPECT_TRUE(v1->Equals(v1));
+ EXPECT_TRUE(v2->Equals(v2));
+ EXPECT_TRUE(v3->Equals(v3));
+
+ EXPECT_FALSE(v1->Equals(v2)); // different operators
+ EXPECT_FALSE(v1->Equals(v3)); // different vector lengths
+}
+
+} // namespace art
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index 320f01a727..5ad011d8f9 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -66,28 +66,215 @@ static bool MayHaveReorderingDependency(SideEffects node, SideEffects other) {
return false;
}
+size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const {
+ DCHECK(heap_location_collector_ != nullptr);
+ size_t heap_loc = heap_location_collector_->GetArrayAccessHeapLocation(array, index);
+ // This array access should be analyzed and added to HeapLocationCollector before.
+ DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
+ return heap_loc;
+}
-// Check whether `node` depends on `other`, taking into account `SideEffect`
-// information and `CanThrow` information.
-static bool HasSideEffectDependency(const HInstruction* node, const HInstruction* other) {
- if (MayHaveReorderingDependency(node->GetSideEffects(), other->GetSideEffects())) {
+bool SchedulingGraph::ArrayAccessMayAlias(const HInstruction* node,
+ const HInstruction* other) const {
+ DCHECK(heap_location_collector_ != nullptr);
+ size_t node_heap_loc = ArrayAccessHeapLocation(node->InputAt(0), node->InputAt(1));
+ size_t other_heap_loc = ArrayAccessHeapLocation(other->InputAt(0), other->InputAt(1));
+
+ // For example: arr[0] and arr[0]
+ if (node_heap_loc == other_heap_loc) {
return true;
}
- if (other->CanThrow() && node->GetSideEffects().DoesAnyWrite()) {
+ // For example: arr[0] and arr[i]
+ if (heap_location_collector_->MayAlias(node_heap_loc, other_heap_loc)) {
return true;
}
- if (other->GetSideEffects().DoesAnyWrite() && node->CanThrow()) {
+ return false;
+}
+
+static bool IsArrayAccess(const HInstruction* instruction) {
+ return instruction->IsArrayGet() || instruction->IsArraySet();
+}
+
+static bool IsInstanceFieldAccess(const HInstruction* instruction) {
+ return instruction->IsInstanceFieldGet() ||
+ instruction->IsInstanceFieldSet() ||
+ instruction->IsUnresolvedInstanceFieldGet() ||
+ instruction->IsUnresolvedInstanceFieldSet();
+}
+
+static bool IsStaticFieldAccess(const HInstruction* instruction) {
+ return instruction->IsStaticFieldGet() ||
+ instruction->IsStaticFieldSet() ||
+ instruction->IsUnresolvedStaticFieldGet() ||
+ instruction->IsUnresolvedStaticFieldSet();
+}
+
+static bool IsResolvedFieldAccess(const HInstruction* instruction) {
+ return instruction->IsInstanceFieldGet() ||
+ instruction->IsInstanceFieldSet() ||
+ instruction->IsStaticFieldGet() ||
+ instruction->IsStaticFieldSet();
+}
+
+static bool IsUnresolvedFieldAccess(const HInstruction* instruction) {
+ return instruction->IsUnresolvedInstanceFieldGet() ||
+ instruction->IsUnresolvedInstanceFieldSet() ||
+ instruction->IsUnresolvedStaticFieldGet() ||
+ instruction->IsUnresolvedStaticFieldSet();
+}
+
+static bool IsFieldAccess(const HInstruction* instruction) {
+ return IsResolvedFieldAccess(instruction) || IsUnresolvedFieldAccess(instruction);
+}
+
+static const FieldInfo* GetFieldInfo(const HInstruction* instruction) {
+ if (instruction->IsInstanceFieldGet()) {
+ return &instruction->AsInstanceFieldGet()->GetFieldInfo();
+ } else if (instruction->IsInstanceFieldSet()) {
+ return &instruction->AsInstanceFieldSet()->GetFieldInfo();
+ } else if (instruction->IsStaticFieldGet()) {
+ return &instruction->AsStaticFieldGet()->GetFieldInfo();
+ } else if (instruction->IsStaticFieldSet()) {
+ return &instruction->AsStaticFieldSet()->GetFieldInfo();
+ } else {
+ LOG(FATAL) << "Unexpected field access type";
+ UNREACHABLE();
+ }
+}
+
+size_t SchedulingGraph::FieldAccessHeapLocation(HInstruction* obj, const FieldInfo* field) const {
+ DCHECK(obj != nullptr);
+ DCHECK(field != nullptr);
+ DCHECK(heap_location_collector_ != nullptr);
+
+ size_t heap_loc = heap_location_collector_->FindHeapLocationIndex(
+ heap_location_collector_->FindReferenceInfoOf(
+ heap_location_collector_->HuntForOriginalReference(obj)),
+ field->GetFieldOffset().SizeValue(),
+ nullptr,
+ field->GetDeclaringClassDefIndex());
+ // This field access should be analyzed and added to HeapLocationCollector before.
+ DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound);
+
+ return heap_loc;
+}
+
+bool SchedulingGraph::FieldAccessMayAlias(const HInstruction* node,
+ const HInstruction* other) const {
+ DCHECK(heap_location_collector_ != nullptr);
+
+ // Static and instance field accesses should not alias.
+ if ((IsInstanceFieldAccess(node) && IsStaticFieldAccess(other)) ||
+ (IsStaticFieldAccess(node) && IsInstanceFieldAccess(other))) {
+ return false;
+ }
+
+ // If either of the field accesses is unresolved.
+ if (IsUnresolvedFieldAccess(node) || IsUnresolvedFieldAccess(other)) {
+ // Conservatively treat these two accesses may alias.
+ return true;
+ }
+
+ // If both fields accesses are resolved.
+ const FieldInfo* node_field = GetFieldInfo(node);
+ const FieldInfo* other_field = GetFieldInfo(other);
+
+ size_t node_loc = FieldAccessHeapLocation(node->InputAt(0), node_field);
+ size_t other_loc = FieldAccessHeapLocation(other->InputAt(0), other_field);
+
+ if (node_loc == other_loc) {
return true;
}
+ if (!heap_location_collector_->MayAlias(node_loc, other_loc)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool SchedulingGraph::HasMemoryDependency(const HInstruction* node,
+ const HInstruction* other) const {
+ if (!MayHaveReorderingDependency(node->GetSideEffects(), other->GetSideEffects())) {
+ return false;
+ }
+
+ if (heap_location_collector_ == nullptr ||
+ heap_location_collector_->GetNumberOfHeapLocations() == 0) {
+ // Without HeapLocation information from load store analysis,
+ // we cannot do further disambiguation analysis on these two instructions.
+ // Just simply say that those two instructions have memory dependency.
+ return true;
+ }
+
+ if (IsArrayAccess(node) && IsArrayAccess(other)) {
+ return ArrayAccessMayAlias(node, other);
+ }
+ if (IsFieldAccess(node) && IsFieldAccess(other)) {
+ return FieldAccessMayAlias(node, other);
+ }
+
+ // TODO(xueliang): LSA to support alias analysis among HVecLoad, HVecStore and ArrayAccess
+ if (node->IsVecMemoryOperation() && other->IsVecMemoryOperation()) {
+ return true;
+ }
+ if (node->IsVecMemoryOperation() && IsArrayAccess(other)) {
+ return true;
+ }
+ if (IsArrayAccess(node) && other->IsVecMemoryOperation()) {
+ return true;
+ }
+
+ // Heap accesses of different kinds should not alias.
+ if (IsArrayAccess(node) && IsFieldAccess(other)) {
+ return false;
+ }
+ if (IsFieldAccess(node) && IsArrayAccess(other)) {
+ return false;
+ }
+ if (node->IsVecMemoryOperation() && IsFieldAccess(other)) {
+ return false;
+ }
+ if (IsFieldAccess(node) && other->IsVecMemoryOperation()) {
+ return false;
+ }
+
+ // We conservatively treat all other cases having dependency,
+ // for example, Invoke and ArrayGet.
+ return true;
+}
+
+bool SchedulingGraph::HasExceptionDependency(const HInstruction* node,
+ const HInstruction* other) const {
+ if (other->CanThrow() && node->GetSideEffects().DoesAnyWrite()) {
+ return true;
+ }
+ if (other->GetSideEffects().DoesAnyWrite() && node->CanThrow()) {
+ return true;
+ }
if (other->CanThrow() && node->CanThrow()) {
return true;
}
- // Check side-effect dependency between ArrayGet and BoundsCheck.
- if (node->IsArrayGet() && other->IsBoundsCheck() && node->InputAt(1) == other) {
+ // Above checks should cover all cases where we cannot reorder two
+ // instructions which may throw exception.
+ return false;
+}
+
+// Check whether `node` depends on `other`, taking into account `SideEffect`
+// information and `CanThrow` information.
+bool SchedulingGraph::HasSideEffectDependency(const HInstruction* node,
+ const HInstruction* other) const {
+ if (HasMemoryDependency(node, other)) {
+ return true;
+ }
+
+ // Even if above memory dependency check has passed, it is still necessary to
+ // check dependencies between instructions that can throw and instructions
+ // that write to memory.
+ if (HasExceptionDependency(node, other)) {
return true;
}
@@ -109,6 +296,10 @@ void SchedulingGraph::AddDependencies(HInstruction* instruction, bool is_schedul
// barrier depend on it.
for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
SchedulingNode* other_node = GetNode(other);
+ CHECK(other_node != nullptr)
+ << other->DebugName()
+ << " is in block " << other->GetBlock()->GetBlockId()
+ << ", and expected in block " << instruction->GetBlock()->GetBlockId();
bool other_is_barrier = other_node->IsSchedulingBarrier();
if (is_scheduling_barrier || other_is_barrier) {
AddOtherDependency(other_node, instruction_node);
@@ -375,8 +566,20 @@ void HScheduler::Schedule(HBasicBlock* block) {
// Build the scheduling graph.
scheduling_graph_.Clear();
+
+ // Only perform LSA/HeapLocation analysis on the basic block that
+ // is going to get instruction scheduled.
+ HeapLocationCollector heap_location_collector(block->GetGraph());
+ heap_location_collector.VisitBasicBlock(block);
+ heap_location_collector.BuildAliasingMatrix();
+ scheduling_graph_.SetHeapLocationCollector(heap_location_collector);
+
for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
+ CHECK_EQ(instruction->GetBlock(), block)
+ << instruction->DebugName()
+ << " is in block " << instruction->GetBlock()->GetBlockId()
+ << ", and expected in block " << block->GetBlockId();
SchedulingNode* node = scheduling_graph_.AddNode(instruction, IsSchedulingBarrier(instruction));
CalculateLatency(node);
scheduling_nodes.push_back(node);
@@ -598,7 +801,9 @@ void HInstructionScheduling::Run(bool only_optimize_loop_blocks,
// Avoid compilation error when compiling for unsupported instruction set.
UNUSED(only_optimize_loop_blocks);
UNUSED(schedule_randomly);
+ UNUSED(codegen_);
#endif
+
switch (instruction_set_) {
#ifdef ART_ENABLE_CODEGEN_arm64
case kArm64: {
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index 73e8087cd0..930a2c82cf 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -21,6 +21,7 @@
#include "base/time_utils.h"
#include "driver/compiler_driver.h"
+#include "load_store_analysis.h"
#include "nodes.h"
#include "optimization.h"
#include "code_generator.h"
@@ -246,7 +247,8 @@ class SchedulingGraph : public ValueObject {
: scheduler_(scheduler),
arena_(arena),
contains_scheduling_barrier_(false),
- nodes_map_(arena_->Adapter(kArenaAllocScheduler)) {}
+ nodes_map_(arena_->Adapter(kArenaAllocScheduler)),
+ heap_location_collector_(nullptr) {}
SchedulingNode* AddNode(HInstruction* instr, bool is_scheduling_barrier = false) {
SchedulingNode* node = new (arena_) SchedulingNode(instr, arena_, is_scheduling_barrier);
@@ -261,6 +263,10 @@ class SchedulingGraph : public ValueObject {
contains_scheduling_barrier_ = false;
}
+ void SetHeapLocationCollector(const HeapLocationCollector& heap_location_collector) {
+ heap_location_collector_ = &heap_location_collector;
+ }
+
SchedulingNode* GetNode(const HInstruction* instr) const {
auto it = nodes_map_.Find(instr);
if (it == nodes_map_.end()) {
@@ -294,6 +300,13 @@ class SchedulingGraph : public ValueObject {
void AddOtherDependency(SchedulingNode* node, SchedulingNode* dependency) {
AddDependency(node, dependency, /*is_data_dependency*/false);
}
+ bool HasMemoryDependency(const HInstruction* node, const HInstruction* other) const;
+ bool HasExceptionDependency(const HInstruction* node, const HInstruction* other) const;
+ bool HasSideEffectDependency(const HInstruction* node, const HInstruction* other) const;
+ bool ArrayAccessMayAlias(const HInstruction* node, const HInstruction* other) const;
+ bool FieldAccessMayAlias(const HInstruction* node, const HInstruction* other) const;
+ size_t ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const;
+ size_t FieldAccessHeapLocation(HInstruction* obj, const FieldInfo* field) const;
// Add dependencies nodes for the given `HInstruction`: inputs, environments, and side-effects.
void AddDependencies(HInstruction* instruction, bool is_scheduling_barrier = false);
@@ -305,6 +318,8 @@ class SchedulingGraph : public ValueObject {
bool contains_scheduling_barrier_;
ArenaHashMap<const HInstruction*, SchedulingNode*> nodes_map_;
+
+ const HeapLocationCollector* heap_location_collector_;
};
/*
@@ -482,10 +497,9 @@ class HInstructionScheduling : public HOptimization {
static constexpr const char* kInstructionScheduling = "scheduler";
+ private:
CodeGenerator* const codegen_;
const InstructionSet instruction_set_;
-
- private:
DISALLOW_COPY_AND_ASSIGN(HInstructionScheduling);
};
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index d87600aa5e..cc7222dd45 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -18,6 +18,7 @@
#include "builder.h"
#include "codegen_test_utils.h"
#include "common_compiler_test.h"
+#include "load_store_analysis.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "pc_relative_fixups_x86.h"
@@ -193,6 +194,147 @@ class SchedulerTest : public CommonCompilerTest {
}
}
+ void TestDependencyGraphOnAliasingArrayAccesses(HScheduler* scheduler) {
+ HBasicBlock* entry = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(entry);
+ graph_->SetEntryBlock(entry);
+ graph_->BuildDominatorTree();
+
+ HInstruction* arr = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ Primitive::kPrimNot);
+ HInstruction* i = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(1),
+ 1,
+ Primitive::kPrimInt);
+ HInstruction* j = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(1),
+ 1,
+ Primitive::kPrimInt);
+ HInstruction* object = new (&allocator_) HParameterValue(graph_->GetDexFile(),
+ dex::TypeIndex(0),
+ 0,
+ Primitive::kPrimNot);
+ HInstruction* c0 = graph_->GetIntConstant(0);
+ HInstruction* c1 = graph_->GetIntConstant(1);
+ HInstruction* add0 = new (&allocator_) HAdd(Primitive::kPrimInt, i, c0);
+ HInstruction* add1 = new (&allocator_) HAdd(Primitive::kPrimInt, i, c1);
+ HInstruction* sub0 = new (&allocator_) HSub(Primitive::kPrimInt, i, c0);
+ HInstruction* sub1 = new (&allocator_) HSub(Primitive::kPrimInt, i, c1);
+ HInstruction* arr_set_0 = new (&allocator_) HArraySet(arr, c0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_1 = new (&allocator_) HArraySet(arr, c1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_i = new (&allocator_) HArraySet(arr, i, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_add0 = new (&allocator_) HArraySet(arr, add0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_add1 = new (&allocator_) HArraySet(arr, add1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_sub0 = new (&allocator_) HArraySet(arr, sub0, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_sub1 = new (&allocator_) HArraySet(arr, sub1, c0, Primitive::kPrimInt, 0);
+ HInstruction* arr_set_j = new (&allocator_) HArraySet(arr, j, c0, Primitive::kPrimInt, 0);
+ HInstanceFieldSet* set_field10 = new (&allocator_) HInstanceFieldSet(object,
+ c1,
+ nullptr,
+ Primitive::kPrimInt,
+ MemberOffset(10),
+ false,
+ kUnknownFieldIndex,
+ kUnknownClassDefIndex,
+ graph_->GetDexFile(),
+ 0);
+
+ HInstruction* block_instructions[] = {arr,
+ i,
+ j,
+ object,
+ add0,
+ add1,
+ sub0,
+ sub1,
+ arr_set_0,
+ arr_set_1,
+ arr_set_i,
+ arr_set_add0,
+ arr_set_add1,
+ arr_set_sub0,
+ arr_set_sub1,
+ arr_set_j,
+ set_field10};
+
+ for (HInstruction* instr : block_instructions) {
+ entry->AddInstruction(instr);
+ }
+
+ SchedulingGraph scheduling_graph(scheduler, graph_->GetArena());
+ HeapLocationCollector heap_location_collector(graph_);
+ heap_location_collector.VisitBasicBlock(entry);
+ heap_location_collector.BuildAliasingMatrix();
+ scheduling_graph.SetHeapLocationCollector(heap_location_collector);
+
+ for (HInstruction* instr : ReverseRange(block_instructions)) {
+ // Build scheduling graph with memory access aliasing information
+ // from LSA/heap_location_collector.
+ scheduling_graph.AddNode(instr);
+ }
+
+ // LSA/HeapLocationCollector should see those ArraySet instructions.
+ ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 9U);
+ ASSERT_TRUE(heap_location_collector.HasHeapStores());
+
+ // Test queries on HeapLocationCollector's aliasing matrix after load store analysis.
+ // HeapLocationCollector and SchedulingGraph should report consistent relationships.
+ size_t loc1 = HeapLocationCollector::kHeapLocationNotFound;
+ size_t loc2 = HeapLocationCollector::kHeapLocationNotFound;
+
+ // Test side effect dependency: array[0] and array[1]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, c0);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, c1);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_1, arr_set_0));
+
+ // Test side effect dependency based on LSA analysis: array[i] and array[j]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, j);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
+
+ // Test side effect dependency based on LSA analysis: array[i] and array[i+0]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, add0);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_add0, arr_set_i));
+
+ // Test side effect dependency based on LSA analysis: array[i] and array[i-0]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, sub0);
+ ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i));
+
+ // Test side effect dependency based on LSA analysis: array[i] and array[i+1]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, add1);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_add1, arr_set_i));
+
+ // Test side effect dependency based on LSA analysis: array[i+1] and array[i-1]
+ loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, add1);
+ loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, sub1);
+ ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2));
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1));
+
+ // Test side effect dependency based on LSA analysis: array[j] and all others array accesses
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_add0));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_sub0));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_add1));
+ ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_sub1));
+
+ // Test that ArraySet and FieldSet should not have side effect dependency
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_i, set_field10));
+ ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, set_field10));
+
+ // Exercise target specific scheduler and SchedulingLatencyVisitor.
+ scheduler->Schedule(graph_);
+ }
+
ArenaPool pool_;
ArenaAllocator allocator_;
HGraph* graph_;
@@ -204,15 +346,28 @@ TEST_F(SchedulerTest, DependencyGraphAndSchedulerARM64) {
arm64::HSchedulerARM64 scheduler(&allocator_, &critical_path_selector);
TestBuildDependencyGraphAndSchedule(&scheduler);
}
+
+TEST_F(SchedulerTest, ArrayAccessAliasingARM64) {
+ CriticalPathSchedulingNodeSelector critical_path_selector;
+ arm64::HSchedulerARM64 scheduler(&allocator_, &critical_path_selector);
+ TestDependencyGraphOnAliasingArrayAccesses(&scheduler);
+}
#endif
#if defined(ART_ENABLE_CODEGEN_arm)
-TEST_F(SchedulerTest, DependencyGrapAndSchedulerARM) {
+TEST_F(SchedulerTest, DependencyGraphAndSchedulerARM) {
CriticalPathSchedulingNodeSelector critical_path_selector;
arm::SchedulingLatencyVisitorARM arm_latency_visitor(/*CodeGenerator*/ nullptr);
arm::HSchedulerARM scheduler(&allocator_, &critical_path_selector, &arm_latency_visitor);
TestBuildDependencyGraphAndSchedule(&scheduler);
}
+
+TEST_F(SchedulerTest, ArrayAccessAliasingARM) {
+ CriticalPathSchedulingNodeSelector critical_path_selector;
+ arm::SchedulingLatencyVisitorARM arm_latency_visitor(/*CodeGenerator*/ nullptr);
+ arm::HSchedulerARM scheduler(&allocator_, &critical_path_selector, &arm_latency_visitor);
+ TestDependencyGraphOnAliasingArrayAccesses(&scheduler);
+}
#endif
TEST_F(SchedulerTest, RandomScheduling) {
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 7b7495bf3b..185303bc8c 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -197,7 +197,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {
HInstruction* instruction = environment->GetInstructionAt(i);
bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
if (should_be_live) {
- DCHECK(instruction->HasSsaIndex());
+ CHECK(instruction->HasSsaIndex()) << instruction->DebugName();
live_in->SetBit(instruction->GetSsaIndex());
}
if (instruction != nullptr) {
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 0b05b752da..44b9bb4eb9 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -4440,6 +4440,106 @@ void MipsAssembler::AdjustBaseAndOffset(Register& base,
CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
}
+void MipsAssembler::AdjustBaseOffsetAndElementSizeShift(Register& base,
+ int32_t& offset,
+ int& element_size_shift) {
+ // This method is used to adjust the base register, offset and element_size_shift
+ // for a vector load/store when the offset doesn't fit into allowed number of bits.
+ // MSA ld.df and st.df instructions take signed offsets as arguments, but maximum
+ // offset is dependant on the size of the data format df (10-bit offsets for ld.b,
+ // 11-bit for ld.h, 12-bit for ld.w and 13-bit for ld.d).
+ // If element_size_shift is non-negative at entry, it won't be changed, but offset
+ // will be checked for appropriate alignment. If negative at entry, it will be
+ // adjusted based on offset for maximum fit.
+ // It's assumed that `base` is a multiple of 8.
+ CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
+
+ if (element_size_shift >= 0) {
+ CHECK_LE(element_size_shift, TIMES_8);
+ CHECK_GE(JAVASTYLE_CTZ(offset), element_size_shift);
+ } else if (IsAligned<kMipsDoublewordSize>(offset)) {
+ element_size_shift = TIMES_8;
+ } else if (IsAligned<kMipsWordSize>(offset)) {
+ element_size_shift = TIMES_4;
+ } else if (IsAligned<kMipsHalfwordSize>(offset)) {
+ element_size_shift = TIMES_2;
+ } else {
+ element_size_shift = TIMES_1;
+ }
+
+ const int low_len = 10 + element_size_shift; // How many low bits of `offset` ld.df/st.df
+ // will take.
+ int16_t low = offset & ((1 << low_len) - 1); // Isolate these bits.
+ low -= (low & (1 << (low_len - 1))) << 1; // Sign-extend these bits.
+ if (low == offset) {
+ return; // `offset` fits into ld.df/st.df.
+ }
+
+ // First, see if `offset` can be represented as a sum of two or three signed offsets.
+ // This can save an instruction or two.
+
+ // Max int16_t that's a multiple of element size.
+ const int32_t kMaxDeltaForSimpleAdjustment = 0x8000 - (1 << element_size_shift);
+ // Max ld.df/st.df offset that's a multiple of element size.
+ const int32_t kMaxLoadStoreOffset = 0x1ff << element_size_shift;
+ const int32_t kMaxOffsetForSimpleAdjustment = kMaxDeltaForSimpleAdjustment + kMaxLoadStoreOffset;
+ const int32_t kMinOffsetForMediumAdjustment = 2 * kMaxDeltaForSimpleAdjustment;
+ const int32_t kMaxOffsetForMediumAdjustment = kMinOffsetForMediumAdjustment + kMaxLoadStoreOffset;
+
+ if (IsInt<16>(offset)) {
+ Addiu(AT, base, offset);
+ offset = 0;
+ } else if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
+ Addiu(AT, base, kMaxDeltaForSimpleAdjustment);
+ offset -= kMaxDeltaForSimpleAdjustment;
+ } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
+ Addiu(AT, base, -kMaxDeltaForSimpleAdjustment);
+ offset += kMaxDeltaForSimpleAdjustment;
+ } else if (!IsR6() && 0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
+ Addiu(AT, base, kMaxDeltaForSimpleAdjustment);
+ if (offset <= kMinOffsetForMediumAdjustment) {
+ Addiu(AT, AT, offset - kMaxDeltaForSimpleAdjustment);
+ offset = 0;
+ } else {
+ Addiu(AT, AT, kMaxDeltaForSimpleAdjustment);
+ offset -= kMinOffsetForMediumAdjustment;
+ }
+ } else if (!IsR6() && -kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
+ Addiu(AT, base, -kMaxDeltaForSimpleAdjustment);
+ if (-kMinOffsetForMediumAdjustment <= offset) {
+ Addiu(AT, AT, offset + kMaxDeltaForSimpleAdjustment);
+ offset = 0;
+ } else {
+ Addiu(AT, AT, -kMaxDeltaForSimpleAdjustment);
+ offset += kMinOffsetForMediumAdjustment;
+ }
+ } else {
+ // 16-bit or smaller parts of `offset`:
+ // |31 hi 16|15 mid 13-10|12-9 low 0|
+ //
+ // Instructions that supply each part as a signed integer addend:
+ // |aui |addiu |ld.df/st.df |
+ uint32_t tmp = static_cast<uint32_t>(offset) - low; // Exclude `low` from the rest of `offset`
+ // (accounts for sign of `low`).
+ tmp += (tmp & (UINT32_C(1) << 15)) << 1; // Account for sign extension in addiu.
+ int16_t mid = Low16Bits(tmp);
+ int16_t hi = High16Bits(tmp);
+ if (IsR6()) {
+ Aui(AT, base, hi);
+ } else {
+ Lui(AT, hi);
+ Addu(AT, AT, base);
+ }
+ if (mid != 0) {
+ Addiu(AT, AT, mid);
+ }
+ offset = low;
+ }
+ base = AT;
+ CHECK_GE(JAVASTYLE_CTZ(offset), element_size_shift);
+ CHECK(IsInt<10>(offset >> element_size_shift));
+}
+
void MipsAssembler::LoadFromOffset(LoadOperandType type,
Register reg,
Register base,
@@ -4455,6 +4555,10 @@ void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset
LoadDFromOffset<>(reg, base, offset);
}
+void MipsAssembler::LoadQFromOffset(FRegister reg, Register base, int32_t offset) {
+ LoadQFromOffset<>(reg, base, offset);
+}
+
void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
size_t size) {
MipsManagedRegister dst = m_dst.AsMips();
@@ -4494,6 +4598,10 @@ void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset)
StoreDToOffset<>(reg, base, offset);
}
+void MipsAssembler::StoreQToOffset(FRegister reg, Register base, int32_t offset) {
+ StoreQToOffset<>(reg, base, offset);
+}
+
static dwarf::Reg DWARFReg(Register reg) {
return dwarf::Reg::MipsCore(static_cast<int>(reg));
}
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index dd4ce6dc80..a229882d18 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -47,14 +47,16 @@ enum LoadOperandType {
kLoadSignedHalfword,
kLoadUnsignedHalfword,
kLoadWord,
- kLoadDoubleword
+ kLoadDoubleword,
+ kLoadQuadword
};
enum StoreOperandType {
kStoreByte,
kStoreHalfword,
kStoreWord,
- kStoreDoubleword
+ kStoreDoubleword,
+ kStoreQuadword
};
// Used to test the values returned by ClassS/ClassD.
@@ -646,6 +648,9 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi
int32_t& offset,
bool is_doubleword,
bool is_float = false);
+ void AdjustBaseOffsetAndElementSizeShift(Register& base,
+ int32_t& offset,
+ int& element_size_shift);
private:
// This will be used as an argument for loads/stores
@@ -793,6 +798,24 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi
}
template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void LoadQFromOffset(FRegister reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ int element_size_shift = -1;
+ AdjustBaseOffsetAndElementSizeShift(base, offset, element_size_shift);
+ switch (element_size_shift) {
+ case TIMES_1: LdB(static_cast<VectorRegister>(reg), base, offset); break;
+ case TIMES_2: LdH(static_cast<VectorRegister>(reg), base, offset); break;
+ case TIMES_4: LdW(static_cast<VectorRegister>(reg), base, offset); break;
+ case TIMES_8: LdD(static_cast<VectorRegister>(reg), base, offset); break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ null_checker();
+ }
+
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
void StoreToOffset(StoreOperandType type,
Register reg,
Register base,
@@ -861,12 +884,32 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler<PointerSi
}
}
+ template <typename ImplicitNullChecker = NoImplicitNullChecker>
+ void StoreQToOffset(FRegister reg,
+ Register base,
+ int32_t offset,
+ ImplicitNullChecker null_checker = NoImplicitNullChecker()) {
+ int element_size_shift = -1;
+ AdjustBaseOffsetAndElementSizeShift(base, offset, element_size_shift);
+ switch (element_size_shift) {
+ case TIMES_1: StB(static_cast<VectorRegister>(reg), base, offset); break;
+ case TIMES_2: StH(static_cast<VectorRegister>(reg), base, offset); break;
+ case TIMES_4: StW(static_cast<VectorRegister>(reg), base, offset); break;
+ case TIMES_8: StD(static_cast<VectorRegister>(reg), base, offset); break;
+ default:
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ null_checker();
+ }
+
void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset);
void LoadSFromOffset(FRegister reg, Register base, int32_t offset);
void LoadDFromOffset(FRegister reg, Register base, int32_t offset);
+ void LoadQFromOffset(FRegister reg, Register base, int32_t offset);
void StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset);
void StoreSToOffset(FRegister reg, Register base, int32_t offset);
void StoreDToOffset(FRegister reg, Register base, int32_t offset);
+ void StoreQToOffset(FRegister reg, Register base, int32_t offset);
// Emit data (e.g. encoded instruction or immediate) to the instruction stream.
void Emit(uint32_t value);
diff --git a/compiler/utils/mips/assembler_mips32r5_test.cc b/compiler/utils/mips/assembler_mips32r5_test.cc
new file mode 100644
index 0000000000..24b09b5524
--- /dev/null
+++ b/compiler/utils/mips/assembler_mips32r5_test.cc
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2017 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 "assembler_mips.h"
+
+#include <map>
+
+#include "base/stl_util.h"
+#include "utils/assembler_test.h"
+
+#define __ GetAssembler()->
+
+namespace art {
+
+struct MIPSCpuRegisterCompare {
+ bool operator()(const mips::Register& a, const mips::Register& b) const {
+ return a < b;
+ }
+};
+
+class AssemblerMIPS32r5Test : public AssemblerTest<mips::MipsAssembler,
+ mips::Register,
+ mips::FRegister,
+ uint32_t,
+ mips::VectorRegister> {
+ public:
+ typedef AssemblerTest<mips::MipsAssembler,
+ mips::Register,
+ mips::FRegister,
+ uint32_t,
+ mips::VectorRegister> Base;
+
+ AssemblerMIPS32r5Test() :
+ instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r5", nullptr)) {
+ }
+
+ protected:
+ // Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
+ std::string GetArchitectureString() OVERRIDE {
+ return "mips";
+ }
+
+ std::string GetAssemblerParameters() OVERRIDE {
+ return " --no-warn -32 -march=mips32r5 -mmsa";
+ }
+
+ void Pad(std::vector<uint8_t>& data) OVERRIDE {
+ // The GNU linker unconditionally pads the code segment with NOPs to a size that is a multiple
+ // of 16 and there doesn't appear to be a way to suppress this padding. Our assembler doesn't
+ // pad, so, in order for two assembler outputs to match, we need to match the padding as well.
+ // NOP is encoded as four zero bytes on MIPS.
+ size_t pad_size = RoundUp(data.size(), 16u) - data.size();
+ data.insert(data.end(), pad_size, 0);
+ }
+
+ std::string GetDisassembleParameters() OVERRIDE {
+ return " -D -bbinary -mmips:isa32r5";
+ }
+
+ mips::MipsAssembler* CreateAssembler(ArenaAllocator* arena) OVERRIDE {
+ return new (arena) mips::MipsAssembler(arena, instruction_set_features_.get());
+ }
+
+ void SetUpHelpers() OVERRIDE {
+ if (registers_.size() == 0) {
+ registers_.push_back(new mips::Register(mips::ZERO));
+ registers_.push_back(new mips::Register(mips::AT));
+ registers_.push_back(new mips::Register(mips::V0));
+ registers_.push_back(new mips::Register(mips::V1));
+ registers_.push_back(new mips::Register(mips::A0));
+ registers_.push_back(new mips::Register(mips::A1));
+ registers_.push_back(new mips::Register(mips::A2));
+ registers_.push_back(new mips::Register(mips::A3));
+ registers_.push_back(new mips::Register(mips::T0));
+ registers_.push_back(new mips::Register(mips::T1));
+ registers_.push_back(new mips::Register(mips::T2));
+ registers_.push_back(new mips::Register(mips::T3));
+ registers_.push_back(new mips::Register(mips::T4));
+ registers_.push_back(new mips::Register(mips::T5));
+ registers_.push_back(new mips::Register(mips::T6));
+ registers_.push_back(new mips::Register(mips::T7));
+ registers_.push_back(new mips::Register(mips::S0));
+ registers_.push_back(new mips::Register(mips::S1));
+ registers_.push_back(new mips::Register(mips::S2));
+ registers_.push_back(new mips::Register(mips::S3));
+ registers_.push_back(new mips::Register(mips::S4));
+ registers_.push_back(new mips::Register(mips::S5));
+ registers_.push_back(new mips::Register(mips::S6));
+ registers_.push_back(new mips::Register(mips::S7));
+ registers_.push_back(new mips::Register(mips::T8));
+ registers_.push_back(new mips::Register(mips::T9));
+ registers_.push_back(new mips::Register(mips::K0));
+ registers_.push_back(new mips::Register(mips::K1));
+ registers_.push_back(new mips::Register(mips::GP));
+ registers_.push_back(new mips::Register(mips::SP));
+ registers_.push_back(new mips::Register(mips::FP));
+ registers_.push_back(new mips::Register(mips::RA));
+
+ secondary_register_names_.emplace(mips::Register(mips::ZERO), "zero");
+ secondary_register_names_.emplace(mips::Register(mips::AT), "at");
+ secondary_register_names_.emplace(mips::Register(mips::V0), "v0");
+ secondary_register_names_.emplace(mips::Register(mips::V1), "v1");
+ secondary_register_names_.emplace(mips::Register(mips::A0), "a0");
+ secondary_register_names_.emplace(mips::Register(mips::A1), "a1");
+ secondary_register_names_.emplace(mips::Register(mips::A2), "a2");
+ secondary_register_names_.emplace(mips::Register(mips::A3), "a3");
+ secondary_register_names_.emplace(mips::Register(mips::T0), "t0");
+ secondary_register_names_.emplace(mips::Register(mips::T1), "t1");
+ secondary_register_names_.emplace(mips::Register(mips::T2), "t2");
+ secondary_register_names_.emplace(mips::Register(mips::T3), "t3");
+ secondary_register_names_.emplace(mips::Register(mips::T4), "t4");
+ secondary_register_names_.emplace(mips::Register(mips::T5), "t5");
+ secondary_register_names_.emplace(mips::Register(mips::T6), "t6");
+ secondary_register_names_.emplace(mips::Register(mips::T7), "t7");
+ secondary_register_names_.emplace(mips::Register(mips::S0), "s0");
+ secondary_register_names_.emplace(mips::Register(mips::S1), "s1");
+ secondary_register_names_.emplace(mips::Register(mips::S2), "s2");
+ secondary_register_names_.emplace(mips::Register(mips::S3), "s3");
+ secondary_register_names_.emplace(mips::Register(mips::S4), "s4");
+ secondary_register_names_.emplace(mips::Register(mips::S5), "s5");
+ secondary_register_names_.emplace(mips::Register(mips::S6), "s6");
+ secondary_register_names_.emplace(mips::Register(mips::S7), "s7");
+ secondary_register_names_.emplace(mips::Register(mips::T8), "t8");
+ secondary_register_names_.emplace(mips::Register(mips::T9), "t9");
+ secondary_register_names_.emplace(mips::Register(mips::K0), "k0");
+ secondary_register_names_.emplace(mips::Register(mips::K1), "k1");
+ secondary_register_names_.emplace(mips::Register(mips::GP), "gp");
+ secondary_register_names_.emplace(mips::Register(mips::SP), "sp");
+ secondary_register_names_.emplace(mips::Register(mips::FP), "fp");
+ secondary_register_names_.emplace(mips::Register(mips::RA), "ra");
+
+ fp_registers_.push_back(new mips::FRegister(mips::F0));
+ fp_registers_.push_back(new mips::FRegister(mips::F1));
+ fp_registers_.push_back(new mips::FRegister(mips::F2));
+ fp_registers_.push_back(new mips::FRegister(mips::F3));
+ fp_registers_.push_back(new mips::FRegister(mips::F4));
+ fp_registers_.push_back(new mips::FRegister(mips::F5));
+ fp_registers_.push_back(new mips::FRegister(mips::F6));
+ fp_registers_.push_back(new mips::FRegister(mips::F7));
+ fp_registers_.push_back(new mips::FRegister(mips::F8));
+ fp_registers_.push_back(new mips::FRegister(mips::F9));
+ fp_registers_.push_back(new mips::FRegister(mips::F10));
+ fp_registers_.push_back(new mips::FRegister(mips::F11));
+ fp_registers_.push_back(new mips::FRegister(mips::F12));
+ fp_registers_.push_back(new mips::FRegister(mips::F13));
+ fp_registers_.push_back(new mips::FRegister(mips::F14));
+ fp_registers_.push_back(new mips::FRegister(mips::F15));
+ fp_registers_.push_back(new mips::FRegister(mips::F16));
+ fp_registers_.push_back(new mips::FRegister(mips::F17));
+ fp_registers_.push_back(new mips::FRegister(mips::F18));
+ fp_registers_.push_back(new mips::FRegister(mips::F19));
+ fp_registers_.push_back(new mips::FRegister(mips::F20));
+ fp_registers_.push_back(new mips::FRegister(mips::F21));
+ fp_registers_.push_back(new mips::FRegister(mips::F22));
+ fp_registers_.push_back(new mips::FRegister(mips::F23));
+ fp_registers_.push_back(new mips::FRegister(mips::F24));
+ fp_registers_.push_back(new mips::FRegister(mips::F25));
+ fp_registers_.push_back(new mips::FRegister(mips::F26));
+ fp_registers_.push_back(new mips::FRegister(mips::F27));
+ fp_registers_.push_back(new mips::FRegister(mips::F28));
+ fp_registers_.push_back(new mips::FRegister(mips::F29));
+ fp_registers_.push_back(new mips::FRegister(mips::F30));
+ fp_registers_.push_back(new mips::FRegister(mips::F31));
+
+ vec_registers_.push_back(new mips::VectorRegister(mips::W0));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W1));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W2));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W3));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W4));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W5));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W6));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W7));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W8));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W9));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W10));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W11));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W12));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W13));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W14));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W15));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W16));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W17));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W18));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W19));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W20));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W21));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W22));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W23));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W24));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W25));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W26));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W27));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W28));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W29));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W30));
+ vec_registers_.push_back(new mips::VectorRegister(mips::W31));
+ }
+ }
+
+ void TearDown() OVERRIDE {
+ AssemblerTest::TearDown();
+ STLDeleteElements(&registers_);
+ STLDeleteElements(&fp_registers_);
+ STLDeleteElements(&vec_registers_);
+ }
+
+ std::vector<mips::Register*> GetRegisters() OVERRIDE {
+ return registers_;
+ }
+
+ std::vector<mips::FRegister*> GetFPRegisters() OVERRIDE {
+ return fp_registers_;
+ }
+
+ std::vector<mips::VectorRegister*> GetVectorRegisters() OVERRIDE {
+ return vec_registers_;
+ }
+
+ uint32_t CreateImmediate(int64_t imm_value) OVERRIDE {
+ return imm_value;
+ }
+
+ std::string GetSecondaryRegisterName(const mips::Register& reg) OVERRIDE {
+ CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end());
+ return secondary_register_names_[reg];
+ }
+
+ std::string RepeatInsn(size_t count, const std::string& insn) {
+ std::string result;
+ for (; count != 0u; --count) {
+ result += insn;
+ }
+ return result;
+ }
+
+ private:
+ std::vector<mips::Register*> registers_;
+ std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_;
+
+ std::vector<mips::FRegister*> fp_registers_;
+ std::vector<mips::VectorRegister*> vec_registers_;
+ std::unique_ptr<const MipsInstructionSetFeatures> instruction_set_features_;
+};
+
+TEST_F(AssemblerMIPS32r5Test, Toolchain) {
+ EXPECT_TRUE(CheckTools());
+}
+
+TEST_F(AssemblerMIPS32r5Test, LoadQFromOffset) {
+ __ LoadQFromOffset(mips::F0, mips::A0, 0);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4);
+ __ LoadQFromOffset(mips::F0, mips::A0, 8);
+ __ LoadQFromOffset(mips::F0, mips::A0, 511);
+ __ LoadQFromOffset(mips::F0, mips::A0, 512);
+ __ LoadQFromOffset(mips::F0, mips::A0, 513);
+ __ LoadQFromOffset(mips::F0, mips::A0, 514);
+ __ LoadQFromOffset(mips::F0, mips::A0, 516);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1022);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1024);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1025);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1026);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1028);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2044);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2048);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2049);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2050);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2052);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4088);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4096);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4097);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4098);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4100);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4104);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFC);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x8000);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x10000);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x12345678);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x12350078);
+ __ LoadQFromOffset(mips::F0, mips::A0, -256);
+ __ LoadQFromOffset(mips::F0, mips::A0, -511);
+ __ LoadQFromOffset(mips::F0, mips::A0, -513);
+ __ LoadQFromOffset(mips::F0, mips::A0, -1022);
+ __ LoadQFromOffset(mips::F0, mips::A0, -1026);
+ __ LoadQFromOffset(mips::F0, mips::A0, -2044);
+ __ LoadQFromOffset(mips::F0, mips::A0, -2052);
+ __ LoadQFromOffset(mips::F0, mips::A0, -4096);
+ __ LoadQFromOffset(mips::F0, mips::A0, -4104);
+ __ LoadQFromOffset(mips::F0, mips::A0, -32768);
+ __ LoadQFromOffset(mips::F0, mips::A0, -36856);
+ __ LoadQFromOffset(mips::F0, mips::A0, 36856);
+ __ LoadQFromOffset(mips::F0, mips::A0, -69608);
+ __ LoadQFromOffset(mips::F0, mips::A0, 69608);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+ const char* expected =
+ "ld.d $w0, 0($a0)\n"
+ "ld.b $w0, 1($a0)\n"
+ "ld.h $w0, 2($a0)\n"
+ "ld.w $w0, 4($a0)\n"
+ "ld.d $w0, 8($a0)\n"
+ "ld.b $w0, 511($a0)\n"
+ "ld.d $w0, 512($a0)\n"
+ "addiu $at, $a0, 513\n"
+ "ld.b $w0, 0($at)\n"
+ "ld.h $w0, 514($a0)\n"
+ "ld.w $w0, 516($a0)\n"
+ "ld.h $w0, 1022($a0)\n"
+ "ld.d $w0, 1024($a0)\n"
+ "addiu $at, $a0, 1025\n"
+ "ld.b $w0, 0($at)\n"
+ "addiu $at, $a0, 1026\n"
+ "ld.h $w0, 0($at)\n"
+ "ld.w $w0, 1028($a0)\n"
+ "ld.w $w0, 2044($a0)\n"
+ "ld.d $w0, 2048($a0)\n"
+ "addiu $at, $a0, 2049\n"
+ "ld.b $w0, 0($at)\n"
+ "addiu $at, $a0, 2050\n"
+ "ld.h $w0, 0($at)\n"
+ "addiu $at, $a0, 2052\n"
+ "ld.w $w0, 0($at)\n"
+ "ld.d $w0, 4088($a0)\n"
+ "addiu $at, $a0, 4096\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, 4097\n"
+ "ld.b $w0, 0($at)\n"
+ "addiu $at, $a0, 4098\n"
+ "ld.h $w0, 0($at)\n"
+ "addiu $at, $a0, 4100\n"
+ "ld.w $w0, 0($at)\n"
+ "addiu $at, $a0, 4104\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FFC\n"
+ "ld.w $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "ld.d $w0, 8($at)\n"
+ "addiu $at, $a0, 32760\n"
+ "addiu $at, $at, 32760\n"
+ "ld.d $w0, 16($at)\n"
+ "lui $at, 4660\n"
+ "addu $at, $at, $a0\n"
+ "addiu $at, $at, 24576\n"
+ "ld.d $w0, -2440($at) # 0xF678\n"
+ "lui $at, 4661\n"
+ "addu $at, $at, $a0\n"
+ "ld.d $w0, 120($at)\n"
+ "ld.d $w0, -256($a0)\n"
+ "ld.b $w0, -511($a0)\n"
+ "addiu $at, $a0, -513\n"
+ "ld.b $w0, 0($at)\n"
+ "ld.h $w0, -1022($a0)\n"
+ "addiu $at, $a0, -1026\n"
+ "ld.h $w0, 0($at)\n"
+ "ld.w $w0, -2044($a0)\n"
+ "addiu $at, $a0, -2052\n"
+ "ld.w $w0, 0($at)\n"
+ "ld.d $w0, -4096($a0)\n"
+ "addiu $at, $a0, -4104\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32768\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32760\n"
+ "addiu $at, $at, -4096\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, 32760\n"
+ "addiu $at, $at, 4096\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32760\n"
+ "addiu $at, $at, -32760\n"
+ "ld.d $w0, -4088($at)\n"
+ "addiu $at, $a0, 32760\n"
+ "addiu $at, $at, 32760\n"
+ "ld.d $w0, 4088($at)\n"
+ "lui $at, 0xABCE\n"
+ "addu $at, $at, $a0\n"
+ "addiu $at, $at, -8192 # 0xE000\n"
+ "ld.d $w0, 0xF00($at)\n"
+ "lui $at, 0x8000\n"
+ "addu $at, $at, $a0\n"
+ "addiu $at, $at, -21504 # 0xAC00\n"
+ "ld.b $w0, -51($at) # 0xFFCD\n";
+ DriverStr(expected, "LoadQFromOffset");
+}
+
+TEST_F(AssemblerMIPS32r5Test, StoreQToOffset) {
+ __ StoreQToOffset(mips::F0, mips::A0, 0);
+ __ StoreQToOffset(mips::F0, mips::A0, 1);
+ __ StoreQToOffset(mips::F0, mips::A0, 2);
+ __ StoreQToOffset(mips::F0, mips::A0, 4);
+ __ StoreQToOffset(mips::F0, mips::A0, 8);
+ __ StoreQToOffset(mips::F0, mips::A0, 511);
+ __ StoreQToOffset(mips::F0, mips::A0, 512);
+ __ StoreQToOffset(mips::F0, mips::A0, 513);
+ __ StoreQToOffset(mips::F0, mips::A0, 514);
+ __ StoreQToOffset(mips::F0, mips::A0, 516);
+ __ StoreQToOffset(mips::F0, mips::A0, 1022);
+ __ StoreQToOffset(mips::F0, mips::A0, 1024);
+ __ StoreQToOffset(mips::F0, mips::A0, 1025);
+ __ StoreQToOffset(mips::F0, mips::A0, 1026);
+ __ StoreQToOffset(mips::F0, mips::A0, 1028);
+ __ StoreQToOffset(mips::F0, mips::A0, 2044);
+ __ StoreQToOffset(mips::F0, mips::A0, 2048);
+ __ StoreQToOffset(mips::F0, mips::A0, 2049);
+ __ StoreQToOffset(mips::F0, mips::A0, 2050);
+ __ StoreQToOffset(mips::F0, mips::A0, 2052);
+ __ StoreQToOffset(mips::F0, mips::A0, 4088);
+ __ StoreQToOffset(mips::F0, mips::A0, 4096);
+ __ StoreQToOffset(mips::F0, mips::A0, 4097);
+ __ StoreQToOffset(mips::F0, mips::A0, 4098);
+ __ StoreQToOffset(mips::F0, mips::A0, 4100);
+ __ StoreQToOffset(mips::F0, mips::A0, 4104);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x7FFC);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x8000);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x10000);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x12345678);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x12350078);
+ __ StoreQToOffset(mips::F0, mips::A0, -256);
+ __ StoreQToOffset(mips::F0, mips::A0, -511);
+ __ StoreQToOffset(mips::F0, mips::A0, -513);
+ __ StoreQToOffset(mips::F0, mips::A0, -1022);
+ __ StoreQToOffset(mips::F0, mips::A0, -1026);
+ __ StoreQToOffset(mips::F0, mips::A0, -2044);
+ __ StoreQToOffset(mips::F0, mips::A0, -2052);
+ __ StoreQToOffset(mips::F0, mips::A0, -4096);
+ __ StoreQToOffset(mips::F0, mips::A0, -4104);
+ __ StoreQToOffset(mips::F0, mips::A0, -32768);
+ __ StoreQToOffset(mips::F0, mips::A0, -36856);
+ __ StoreQToOffset(mips::F0, mips::A0, 36856);
+ __ StoreQToOffset(mips::F0, mips::A0, -69608);
+ __ StoreQToOffset(mips::F0, mips::A0, 69608);
+ __ StoreQToOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+ const char* expected =
+ "st.d $w0, 0($a0)\n"
+ "st.b $w0, 1($a0)\n"
+ "st.h $w0, 2($a0)\n"
+ "st.w $w0, 4($a0)\n"
+ "st.d $w0, 8($a0)\n"
+ "st.b $w0, 511($a0)\n"
+ "st.d $w0, 512($a0)\n"
+ "addiu $at, $a0, 513\n"
+ "st.b $w0, 0($at)\n"
+ "st.h $w0, 514($a0)\n"
+ "st.w $w0, 516($a0)\n"
+ "st.h $w0, 1022($a0)\n"
+ "st.d $w0, 1024($a0)\n"
+ "addiu $at, $a0, 1025\n"
+ "st.b $w0, 0($at)\n"
+ "addiu $at, $a0, 1026\n"
+ "st.h $w0, 0($at)\n"
+ "st.w $w0, 1028($a0)\n"
+ "st.w $w0, 2044($a0)\n"
+ "st.d $w0, 2048($a0)\n"
+ "addiu $at, $a0, 2049\n"
+ "st.b $w0, 0($at)\n"
+ "addiu $at, $a0, 2050\n"
+ "st.h $w0, 0($at)\n"
+ "addiu $at, $a0, 2052\n"
+ "st.w $w0, 0($at)\n"
+ "st.d $w0, 4088($a0)\n"
+ "addiu $at, $a0, 4096\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, 4097\n"
+ "st.b $w0, 0($at)\n"
+ "addiu $at, $a0, 4098\n"
+ "st.h $w0, 0($at)\n"
+ "addiu $at, $a0, 4100\n"
+ "st.w $w0, 0($at)\n"
+ "addiu $at, $a0, 4104\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FFC\n"
+ "st.w $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "st.d $w0, 8($at)\n"
+ "addiu $at, $a0, 32760\n"
+ "addiu $at, $at, 32760\n"
+ "st.d $w0, 16($at)\n"
+ "lui $at, 4660\n"
+ "addu $at, $at, $a0\n"
+ "addiu $at, $at, 24576\n"
+ "st.d $w0, -2440($at) # 0xF678\n"
+ "lui $at, 4661\n"
+ "addu $at, $at, $a0\n"
+ "st.d $w0, 120($at)\n"
+ "st.d $w0, -256($a0)\n"
+ "st.b $w0, -511($a0)\n"
+ "addiu $at, $a0, -513\n"
+ "st.b $w0, 0($at)\n"
+ "st.h $w0, -1022($a0)\n"
+ "addiu $at, $a0, -1026\n"
+ "st.h $w0, 0($at)\n"
+ "st.w $w0, -2044($a0)\n"
+ "addiu $at, $a0, -2052\n"
+ "st.w $w0, 0($at)\n"
+ "st.d $w0, -4096($a0)\n"
+ "addiu $at, $a0, -4104\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32768\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32760\n"
+ "addiu $at, $at, -4096\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, 32760\n"
+ "addiu $at, $at, 4096\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32760\n"
+ "addiu $at, $at, -32760\n"
+ "st.d $w0, -4088($at)\n"
+ "addiu $at, $a0, 32760\n"
+ "addiu $at, $at, 32760\n"
+ "st.d $w0, 4088($at)\n"
+ "lui $at, 0xABCE\n"
+ "addu $at, $at, $a0\n"
+ "addiu $at, $at, -8192 # 0xE000\n"
+ "st.d $w0, 0xF00($at)\n"
+ "lui $at, 0x8000\n"
+ "addu $at, $at, $a0\n"
+ "addiu $at, $at, -21504 # 0xAC00\n"
+ "st.b $w0, -51($at) # 0xFFCD\n";
+ DriverStr(expected, "StoreQToOffset");
+}
+
+#undef __
+} // namespace art
diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc
index d4642607ad..6ee2a5cb79 100644
--- a/compiler/utils/mips/assembler_mips32r6_test.cc
+++ b/compiler/utils/mips/assembler_mips32r6_test.cc
@@ -627,6 +627,124 @@ TEST_F(AssemblerMIPS32r6Test, LoadDFromOffset) {
DriverStr(expected, "LoadDFromOffset");
}
+TEST_F(AssemblerMIPS32r6Test, LoadQFromOffset) {
+ __ LoadQFromOffset(mips::F0, mips::A0, 0);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4);
+ __ LoadQFromOffset(mips::F0, mips::A0, 8);
+ __ LoadQFromOffset(mips::F0, mips::A0, 511);
+ __ LoadQFromOffset(mips::F0, mips::A0, 512);
+ __ LoadQFromOffset(mips::F0, mips::A0, 513);
+ __ LoadQFromOffset(mips::F0, mips::A0, 514);
+ __ LoadQFromOffset(mips::F0, mips::A0, 516);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1022);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1024);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1025);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1026);
+ __ LoadQFromOffset(mips::F0, mips::A0, 1028);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2044);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2048);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2049);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2050);
+ __ LoadQFromOffset(mips::F0, mips::A0, 2052);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4088);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4096);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4097);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4098);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4100);
+ __ LoadQFromOffset(mips::F0, mips::A0, 4104);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFC);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x8000);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x10000);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x12345678);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x12350078);
+ __ LoadQFromOffset(mips::F0, mips::A0, -256);
+ __ LoadQFromOffset(mips::F0, mips::A0, -511);
+ __ LoadQFromOffset(mips::F0, mips::A0, -513);
+ __ LoadQFromOffset(mips::F0, mips::A0, -1022);
+ __ LoadQFromOffset(mips::F0, mips::A0, -1026);
+ __ LoadQFromOffset(mips::F0, mips::A0, -2044);
+ __ LoadQFromOffset(mips::F0, mips::A0, -2052);
+ __ LoadQFromOffset(mips::F0, mips::A0, -4096);
+ __ LoadQFromOffset(mips::F0, mips::A0, -4104);
+ __ LoadQFromOffset(mips::F0, mips::A0, -32768);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ LoadQFromOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+ const char* expected =
+ "ld.d $w0, 0($a0)\n"
+ "ld.b $w0, 1($a0)\n"
+ "ld.h $w0, 2($a0)\n"
+ "ld.w $w0, 4($a0)\n"
+ "ld.d $w0, 8($a0)\n"
+ "ld.b $w0, 511($a0)\n"
+ "ld.d $w0, 512($a0)\n"
+ "addiu $at, $a0, 513\n"
+ "ld.b $w0, 0($at)\n"
+ "ld.h $w0, 514($a0)\n"
+ "ld.w $w0, 516($a0)\n"
+ "ld.h $w0, 1022($a0)\n"
+ "ld.d $w0, 1024($a0)\n"
+ "addiu $at, $a0, 1025\n"
+ "ld.b $w0, 0($at)\n"
+ "addiu $at, $a0, 1026\n"
+ "ld.h $w0, 0($at)\n"
+ "ld.w $w0, 1028($a0)\n"
+ "ld.w $w0, 2044($a0)\n"
+ "ld.d $w0, 2048($a0)\n"
+ "addiu $at, $a0, 2049\n"
+ "ld.b $w0, 0($at)\n"
+ "addiu $at, $a0, 2050\n"
+ "ld.h $w0, 0($at)\n"
+ "addiu $at, $a0, 2052\n"
+ "ld.w $w0, 0($at)\n"
+ "ld.d $w0, 4088($a0)\n"
+ "addiu $at, $a0, 4096\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, 4097\n"
+ "ld.b $w0, 0($at)\n"
+ "addiu $at, $a0, 4098\n"
+ "ld.h $w0, 0($at)\n"
+ "addiu $at, $a0, 4100\n"
+ "ld.w $w0, 0($at)\n"
+ "addiu $at, $a0, 4104\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FFC\n"
+ "ld.w $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "ld.d $w0, 8($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "ld.d $w0, 0($at)\n"
+ "aui $at, $a0, 0x1234\n"
+ "addiu $at, $at, 0x6000\n"
+ "ld.d $w0, -2440($at) # 0xF678\n"
+ "aui $at, $a0, 0x1235\n"
+ "ld.d $w0, 0x78($at)\n"
+ "ld.d $w0, -256($a0)\n"
+ "ld.b $w0, -511($a0)\n"
+ "addiu $at, $a0, -513\n"
+ "ld.b $w0, 0($at)\n"
+ "ld.h $w0, -1022($a0)\n"
+ "addiu $at, $a0, -1026\n"
+ "ld.h $w0, 0($at)\n"
+ "ld.w $w0, -2044($a0)\n"
+ "addiu $at, $a0, -2052\n"
+ "ld.w $w0, 0($at)\n"
+ "ld.d $w0, -4096($a0)\n"
+ "addiu $at, $a0, -4104\n"
+ "ld.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32768\n"
+ "ld.d $w0, 0($at)\n"
+ "aui $at, $a0, 0xABCE\n"
+ "addiu $at, $at, -8192 # 0xE000\n"
+ "ld.d $w0, 0xF00($at)\n"
+ "aui $at, $a0, 0x8000\n"
+ "addiu $at, $at, -21504 # 0xAC00\n"
+ "ld.b $w0, -51($at) # 0xFFCD\n";
+ DriverStr(expected, "LoadQFromOffset");
+}
+
TEST_F(AssemblerMIPS32r6Test, StoreDToOffset) {
__ StoreDToOffset(mips::F0, mips::A0, -0x8000);
__ StoreDToOffset(mips::F0, mips::A0, +0);
@@ -711,6 +829,124 @@ TEST_F(AssemblerMIPS32r6Test, StoreDToOffset) {
DriverStr(expected, "StoreDToOffset");
}
+TEST_F(AssemblerMIPS32r6Test, StoreQToOffset) {
+ __ StoreQToOffset(mips::F0, mips::A0, 0);
+ __ StoreQToOffset(mips::F0, mips::A0, 1);
+ __ StoreQToOffset(mips::F0, mips::A0, 2);
+ __ StoreQToOffset(mips::F0, mips::A0, 4);
+ __ StoreQToOffset(mips::F0, mips::A0, 8);
+ __ StoreQToOffset(mips::F0, mips::A0, 511);
+ __ StoreQToOffset(mips::F0, mips::A0, 512);
+ __ StoreQToOffset(mips::F0, mips::A0, 513);
+ __ StoreQToOffset(mips::F0, mips::A0, 514);
+ __ StoreQToOffset(mips::F0, mips::A0, 516);
+ __ StoreQToOffset(mips::F0, mips::A0, 1022);
+ __ StoreQToOffset(mips::F0, mips::A0, 1024);
+ __ StoreQToOffset(mips::F0, mips::A0, 1025);
+ __ StoreQToOffset(mips::F0, mips::A0, 1026);
+ __ StoreQToOffset(mips::F0, mips::A0, 1028);
+ __ StoreQToOffset(mips::F0, mips::A0, 2044);
+ __ StoreQToOffset(mips::F0, mips::A0, 2048);
+ __ StoreQToOffset(mips::F0, mips::A0, 2049);
+ __ StoreQToOffset(mips::F0, mips::A0, 2050);
+ __ StoreQToOffset(mips::F0, mips::A0, 2052);
+ __ StoreQToOffset(mips::F0, mips::A0, 4088);
+ __ StoreQToOffset(mips::F0, mips::A0, 4096);
+ __ StoreQToOffset(mips::F0, mips::A0, 4097);
+ __ StoreQToOffset(mips::F0, mips::A0, 4098);
+ __ StoreQToOffset(mips::F0, mips::A0, 4100);
+ __ StoreQToOffset(mips::F0, mips::A0, 4104);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x7FFC);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x8000);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x10000);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x12345678);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x12350078);
+ __ StoreQToOffset(mips::F0, mips::A0, -256);
+ __ StoreQToOffset(mips::F0, mips::A0, -511);
+ __ StoreQToOffset(mips::F0, mips::A0, -513);
+ __ StoreQToOffset(mips::F0, mips::A0, -1022);
+ __ StoreQToOffset(mips::F0, mips::A0, -1026);
+ __ StoreQToOffset(mips::F0, mips::A0, -2044);
+ __ StoreQToOffset(mips::F0, mips::A0, -2052);
+ __ StoreQToOffset(mips::F0, mips::A0, -4096);
+ __ StoreQToOffset(mips::F0, mips::A0, -4104);
+ __ StoreQToOffset(mips::F0, mips::A0, -32768);
+ __ StoreQToOffset(mips::F0, mips::A0, 0xABCDEF00);
+ __ StoreQToOffset(mips::F0, mips::A0, 0x7FFFABCD);
+
+ const char* expected =
+ "st.d $w0, 0($a0)\n"
+ "st.b $w0, 1($a0)\n"
+ "st.h $w0, 2($a0)\n"
+ "st.w $w0, 4($a0)\n"
+ "st.d $w0, 8($a0)\n"
+ "st.b $w0, 511($a0)\n"
+ "st.d $w0, 512($a0)\n"
+ "addiu $at, $a0, 513\n"
+ "st.b $w0, 0($at)\n"
+ "st.h $w0, 514($a0)\n"
+ "st.w $w0, 516($a0)\n"
+ "st.h $w0, 1022($a0)\n"
+ "st.d $w0, 1024($a0)\n"
+ "addiu $at, $a0, 1025\n"
+ "st.b $w0, 0($at)\n"
+ "addiu $at, $a0, 1026\n"
+ "st.h $w0, 0($at)\n"
+ "st.w $w0, 1028($a0)\n"
+ "st.w $w0, 2044($a0)\n"
+ "st.d $w0, 2048($a0)\n"
+ "addiu $at, $a0, 2049\n"
+ "st.b $w0, 0($at)\n"
+ "addiu $at, $a0, 2050\n"
+ "st.h $w0, 0($at)\n"
+ "addiu $at, $a0, 2052\n"
+ "st.w $w0, 0($at)\n"
+ "st.d $w0, 4088($a0)\n"
+ "addiu $at, $a0, 4096\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, 4097\n"
+ "st.b $w0, 0($at)\n"
+ "addiu $at, $a0, 4098\n"
+ "st.h $w0, 0($at)\n"
+ "addiu $at, $a0, 4100\n"
+ "st.w $w0, 0($at)\n"
+ "addiu $at, $a0, 4104\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FFC\n"
+ "st.w $w0, 0($at)\n"
+ "addiu $at, $a0, 0x7FF8\n"
+ "st.d $w0, 8($at)\n"
+ "aui $at, $a0, 0x1\n"
+ "st.d $w0, 0($at)\n"
+ "aui $at, $a0, 0x1234\n"
+ "addiu $at, $at, 0x6000\n"
+ "st.d $w0, -2440($at) # 0xF678\n"
+ "aui $at, $a0, 0x1235\n"
+ "st.d $w0, 0x78($at)\n"
+ "st.d $w0, -256($a0)\n"
+ "st.b $w0, -511($a0)\n"
+ "addiu $at, $a0, -513\n"
+ "st.b $w0, 0($at)\n"
+ "st.h $w0, -1022($a0)\n"
+ "addiu $at, $a0, -1026\n"
+ "st.h $w0, 0($at)\n"
+ "st.w $w0, -2044($a0)\n"
+ "addiu $at, $a0, -2052\n"
+ "st.w $w0, 0($at)\n"
+ "st.d $w0, -4096($a0)\n"
+ "addiu $at, $a0, -4104\n"
+ "st.d $w0, 0($at)\n"
+ "addiu $at, $a0, -32768\n"
+ "st.d $w0, 0($at)\n"
+ "aui $at, $a0, 0xABCE\n"
+ "addiu $at, $at, -8192 # 0xE000\n"
+ "st.d $w0, 0xF00($at)\n"
+ "aui $at, $a0, 0x8000\n"
+ "addiu $at, $at, -21504 # 0xAC00\n"
+ "st.b $w0, -51($at) # 0xFFCD\n";
+ DriverStr(expected, "StoreQToOffset");
+}
+
TEST_F(AssemblerMIPS32r6Test, LoadFarthestNearLabelAddress) {
mips::MipsLabel label;
__ LoadLabelAddress(mips::V0, mips::ZERO, &label);
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 92d60b252b..ca0bae1c8e 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -925,8 +925,6 @@ class Dex2Oat FINAL {
break;
}
- compiler_options_->verbose_methods_ = verbose_methods_.empty() ? nullptr : &verbose_methods_;
-
if (!IsBootImage() && multi_image_) {
Usage("--multi-image can only be used when creating boot images");
}
@@ -1262,11 +1260,6 @@ class Dex2Oat FINAL {
app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
} else if (option.starts_with("--app-image-fd=")) {
ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
- } else if (option.starts_with("--verbose-methods=")) {
- // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
- // conditional on having verbost methods.
- gLogVerbosity.compiler = false;
- Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
} else if (option == "--multi-image") {
multi_image_ = true;
} else if (option.starts_with("--no-inline-from=")) {
@@ -2804,7 +2797,6 @@ class Dex2Oat FINAL {
std::vector<const DexFile*> no_inline_from_dex_files_;
- std::vector<std::string> verbose_methods_;
bool dump_stats_;
bool dump_passes_;
bool dump_timing_;
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index df0169f7d0..8437ea5dc0 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1582,31 +1582,53 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) {
static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
const DexFile::MethodHandleItem& mh = pDexFile->GetMethodHandle(idx);
+ const char* type = nullptr;
+ bool is_instance = false;
bool is_invoke = false;
- const char* type;
switch (static_cast<DexFile::MethodHandleType>(mh.method_handle_type_)) {
case DexFile::MethodHandleType::kStaticPut:
type = "put-static";
+ is_instance = false;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kStaticGet:
type = "get-static";
+ is_instance = false;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kInstancePut:
type = "put-instance";
+ is_instance = true;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kInstanceGet:
type = "get-instance";
+ is_instance = true;
+ is_invoke = false;
break;
case DexFile::MethodHandleType::kInvokeStatic:
type = "invoke-static";
+ is_instance = false;
is_invoke = true;
break;
case DexFile::MethodHandleType::kInvokeInstance:
type = "invoke-instance";
+ is_instance = true;
is_invoke = true;
break;
case DexFile::MethodHandleType::kInvokeConstructor:
type = "invoke-constructor";
+ is_instance = true;
+ is_invoke = true;
+ break;
+ case DexFile::MethodHandleType::kInvokeDirect:
+ type = "invoke-direct";
+ is_instance = true;
+ is_invoke = true;
+ break;
+ case DexFile::MethodHandleType::kInvokeInterface:
+ type = "invoke-interface";
+ is_instance = true;
is_invoke = true;
break;
}
@@ -1614,16 +1636,26 @@ static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) {
const char* declaring_class;
const char* member;
std::string member_type;
- if (is_invoke) {
- const DexFile::MethodId& method_id = pDexFile->GetMethodId(mh.field_or_method_idx_);
- declaring_class = pDexFile->GetMethodDeclaringClassDescriptor(method_id);
- member = pDexFile->GetMethodName(method_id);
- member_type = pDexFile->GetMethodSignature(method_id).ToString();
+ if (type != nullptr) {
+ if (is_invoke) {
+ const DexFile::MethodId& method_id = pDexFile->GetMethodId(mh.field_or_method_idx_);
+ declaring_class = pDexFile->GetMethodDeclaringClassDescriptor(method_id);
+ member = pDexFile->GetMethodName(method_id);
+ member_type = pDexFile->GetMethodSignature(method_id).ToString();
+ } else {
+ const DexFile::FieldId& field_id = pDexFile->GetFieldId(mh.field_or_method_idx_);
+ declaring_class = pDexFile->GetFieldDeclaringClassDescriptor(field_id);
+ member = pDexFile->GetFieldName(field_id);
+ member_type = pDexFile->GetFieldTypeDescriptor(field_id);
+ }
+ if (is_instance) {
+ member_type = android::base::StringPrintf("(%s%s", declaring_class, member_type.c_str() + 1);
+ }
} else {
- const DexFile::FieldId& field_id = pDexFile->GetFieldId(mh.field_or_method_idx_);
- declaring_class = pDexFile->GetFieldDeclaringClassDescriptor(field_id);
- member = pDexFile->GetFieldName(field_id);
- member_type = pDexFile->GetFieldTypeDescriptor(field_id);
+ type = "?";
+ declaring_class = "?";
+ member = "?";
+ member_type = "?";
}
if (gOptions.outputFormat == OUTPUT_PLAIN) {
@@ -1661,12 +1693,12 @@ static void dumpCallSite(const DexFile* pDexFile, u4 idx) {
it.Next();
if (gOptions.outputFormat == OUTPUT_PLAIN) {
- fprintf(gOutFile, "Call site #%u:\n", idx);
+ fprintf(gOutFile, "Call site #%u: // offset %u\n", idx, call_site_id.data_off_);
fprintf(gOutFile, " link_argument[0] : %u (MethodHandle)\n", method_handle_idx);
fprintf(gOutFile, " link_argument[1] : %s (String)\n", method_name);
fprintf(gOutFile, " link_argument[2] : %s (MethodType)\n", method_type.c_str());
} else {
- fprintf(gOutFile, "<call_site index=\"%u\">\n", idx);
+ fprintf(gOutFile, "<call_site index=\"%u\" offset=\"%u\">\n", idx, call_site_id.data_off_);
fprintf(gOutFile,
"<link_argument index=\"0\" type=\"MethodHandle\" value=\"%u\"/>\n",
method_handle_idx);
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index a200d8d9c7..5913832f96 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -793,8 +793,10 @@ void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) {
static_cast<DexFile::MethodHandleType>(disk_method_handle.method_handle_type_);
bool is_invoke = type == DexFile::MethodHandleType::kInvokeStatic ||
type == DexFile::MethodHandleType::kInvokeInstance ||
- type == DexFile::MethodHandleType::kInvokeConstructor;
- static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeConstructor,
+ type == DexFile::MethodHandleType::kInvokeConstructor ||
+ type == DexFile::MethodHandleType::kInvokeDirect ||
+ type == DexFile::MethodHandleType::kInvokeInterface;
+ static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeInterface,
"Unexpected method handle types.");
IndexedItem* field_or_method_id;
if (is_invoke) {
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 531bc98a0c..ed011d6771 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -58,7 +58,8 @@ class Options {
bool show_section_headers_ = false;
bool show_section_statistics_ = false;
bool verbose_ = false;
- bool verify_output_ = false;
+ // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842.
+ bool verify_output_ = true;
bool visualize_pattern_ = false;
OutputFormat output_format_ = kOutputPlain;
const char* output_dex_directory_ = nullptr;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index d8bafc011a..066c66ac93 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -3466,7 +3466,7 @@ struct OatdumpArgs : public CmdlineArgs {
" Example: --image=/system/framework/boot.art\n"
"\n"
" --app-image=<file.art>: specifies an input app image. Must also have a specified\n"
- " boot image and app oat file.\n"
+ " boot image (with --image) and app oat file (with --app-oat).\n"
" Example: --app-image=app.art\n"
"\n"
" --app-oat=<file.odex>: specifies an input app oat.\n"
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 46307ddde8..0dfc60d88a 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -461,6 +461,7 @@ gensrcs {
"object_callbacks.h",
"process_state.h",
"stack.h",
+ "suspend_reason.h",
"thread.h",
"thread_state.h",
"ti/agent.h",
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h
index 2623ee9315..fa9aa46d4d 100644
--- a/runtime/arch/arm/context_arm.h
+++ b/runtime/arch/arm/context_arm.h
@@ -25,7 +25,7 @@
namespace art {
namespace arm {
-class ArmContext : public Context {
+class ArmContext FINAL : public Context {
public:
ArmContext() {
Reset();
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index 105e78461d..36aded07c4 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -25,7 +25,7 @@
namespace art {
namespace arm64 {
-class Arm64Context : public Context {
+class Arm64Context FINAL : public Context {
public:
Arm64Context() {
Reset();
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index ee91277417..138dbf9495 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2669,19 +2669,19 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29
RESTORE_TWO_REGS x14, x15, 112
RESTORE_TWO_REGS x18, x19, 128 // Skip x16, x17, i.e. IP0, IP1.
RESTORE_REG xLR, 144 // Restore return address.
- // Save all potentially live caller-save floating-point registers.
- stp d0, d1, [sp, #160]
- stp d2, d3, [sp, #176]
- stp d4, d5, [sp, #192]
- stp d6, d7, [sp, #208]
- stp d16, d17, [sp, #224]
- stp d18, d19, [sp, #240]
- stp d20, d21, [sp, #256]
- stp d22, d23, [sp, #272]
- stp d24, d25, [sp, #288]
- stp d26, d27, [sp, #304]
- stp d28, d29, [sp, #320]
- stp d30, d31, [sp, #336]
+ // Restore caller-save floating-point registers.
+ ldp d0, d1, [sp, #160]
+ ldp d2, d3, [sp, #176]
+ ldp d4, d5, [sp, #192]
+ ldp d6, d7, [sp, #208]
+ ldp d16, d17, [sp, #224]
+ ldp d18, d19, [sp, #240]
+ ldp d20, d21, [sp, #256]
+ ldp d22, d23, [sp, #272]
+ ldp d24, d25, [sp, #288]
+ ldp d26, d27, [sp, #304]
+ ldp d28, d29, [sp, #320]
+ ldp d30, d31, [sp, #336]
ldr x0, [lr, #\ldr_offset] // Load the instruction.
adr xIP1, .Lmark_introspection_return_switch
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index f482d9ffcb..303dfe361c 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -25,7 +25,7 @@
namespace art {
namespace x86 {
-class X86Context : public Context {
+class X86Context FINAL : public Context {
public:
X86Context() {
Reset();
diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h
index 46f2b63848..f8e2845983 100644
--- a/runtime/arch/x86_64/context_x86_64.h
+++ b/runtime/arch/x86_64/context_x86_64.h
@@ -25,7 +25,7 @@
namespace art {
namespace x86_64 {
-class X86_64Context : public Context {
+class X86_64Context FINAL : public Context {
public:
X86_64Context() {
Reset();
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 155498639e..3e7ed9799d 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -61,6 +61,19 @@ DEFINE_RUNTIME_DEBUG_FLAG(ArtMethod, kCheckDeclaringClassState);
static_assert(ArtMethod::kRuntimeMethodDexMethodIndex == DexFile::kDexNoIndex,
"Wrong runtime-method dex method index");
+ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) {
+ if (LIKELY(!IsDefault())) {
+ return this;
+ } else {
+ mirror::Class* declaring_class = GetDeclaringClass();
+ ArtMethod* ret = declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
+ GetDexMethodIndex(),
+ pointer_size);
+ DCHECK(ret != nullptr);
+ return ret;
+ }
+}
+
ArtMethod* ArtMethod::GetNonObsoleteMethod() {
DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
if (LIKELY(!IsObsolete())) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 96306af177..4b3e8efdad 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -483,6 +483,12 @@ class ArtMethod FINAL {
}
}
+ // Takes a method and returns a 'canonical' one if the method is default (and therefore
+ // potentially copied from some other class). For example, this ensures that the debugger does not
+ // get confused as to which method we are in.
+ ArtMethod* GetCanonicalMethod(PointerSize pointer_size = kRuntimePointerSize)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ArtMethod* GetSingleImplementation(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 928645ac0f..a19085f5b5 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -55,6 +55,7 @@
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
+#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
@@ -88,6 +89,7 @@
#include "mirror/method_handles_lookup.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/object-refvisitor-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
@@ -1193,6 +1195,63 @@ class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
gc::accounting::HeapBitmap* const live_bitmap_;
};
+class FixupInternVisitor {
+ public:
+ ALWAYS_INLINE ObjPtr<mirror::Object> TryInsertIntern(mirror::Object* obj) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj != nullptr && obj->IsString()) {
+ const auto intern = Runtime::Current()->GetInternTable()->InternStrong(obj->AsString());
+ return intern;
+ }
+ return obj;
+ }
+
+ ALWAYS_INLINE void VisitRootIfNonNull(
+ mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ ALWAYS_INLINE void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ root->Assign(TryInsertIntern(root->AsMirrorPtr()));
+ }
+
+ // Visit Class Fields
+ ALWAYS_INLINE void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // There could be overlap between ranges, we must avoid visiting the same reference twice.
+ // Avoid the class field since we already fixed it up in FixupClassVisitor.
+ if (offset.Uint32Value() != mirror::Object::ClassOffset().Uint32Value()) {
+ // Updating images, don't do a read barrier.
+ // Only string fields are fixed, don't do a verify.
+ mirror::Object* ref = obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(
+ offset);
+ obj->SetFieldObject<false, false>(offset, TryInsertIntern(ref));
+ }
+ }
+
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
+ this->operator()(ref, mirror::Reference::ReferentOffset(), false);
+ }
+
+ void operator()(mirror::Object* obj) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (obj->IsDexCache()) {
+ obj->VisitReferences<true, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+ } else {
+ // Don't visit native roots for non-dex-cache
+ obj->VisitReferences<false, kVerifyNone, kWithoutReadBarrier>(*this, *this);
+ }
+ }
+};
+
// Copies data from one array to another array at the same position
// if pred returns false. If there is a page of continuous data in
// the src array for which pred consistently returns true then
@@ -1285,6 +1344,7 @@ bool AppImageClassLoadersAndDexCachesHelper::Update(
return false;
}
}
+
// Only add the classes to the class loader after the points where we can return false.
for (size_t i = 0; i < num_dex_caches; i++) {
ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
@@ -1448,6 +1508,21 @@ bool AppImageClassLoadersAndDexCachesHelper::Update(
}
}
}
+ {
+ // Fixup all the literal strings happens at app images which are supposed to be interned.
+ ScopedTrace timing("Fixup String Intern in image and dex_cache");
+ const auto& image_header = space->GetImageHeader();
+ const auto bitmap = space->GetMarkBitmap(); // bitmap of objects
+ const uint8_t* target_base = space->GetMemMap()->Begin();
+ const ImageSection& objects_section =
+ image_header.GetImageSection(ImageHeader::kSectionObjects);
+
+ uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
+ uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
+
+ FixupInternVisitor fixup_intern_visitor;
+ bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
+ }
if (*out_forward_dex_cache_array) {
ScopedTrace timing("Fixup ArtMethod dex cache arrays");
FixupArtMethodArrayVisitor visitor(header);
@@ -2410,74 +2485,121 @@ ClassPathEntry FindInClassPath(const char* descriptor,
return ClassPathEntry(nullptr, nullptr);
}
+// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
+// (they both have the same behaviour with respect to class lockup order)
+static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return
+ (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+ (class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+}
+
+static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* class_loader_class = class_loader->GetClass();
+ return class_loader_class ==
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+}
+
bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
ObjPtr<mirror::Class>* result) {
- // Termination case: boot class-loader.
+ // Termination case: boot class loader.
if (IsBootClassLoader(soa, class_loader.Get())) {
- // The boot class loader, search the boot class path.
- ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
- if (pair.second != nullptr) {
- ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
- if (klass != nullptr) {
- *result = EnsureResolved(self, descriptor, klass);
- } else {
- *result = DefineClass(self,
- descriptor,
- hash,
- ScopedNullHandle<mirror::ClassLoader>(),
- *pair.first,
- *pair.second);
- }
- if (*result == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- }
- } else {
- *result = nullptr;
- }
+ *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
return true;
}
- // Unsupported class-loader?
- if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
- class_loader->GetClass()) {
- // PathClassLoader is the most common case, so it's the one we check first. For secondary dex
- // files, we also check DexClassLoader here.
- if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader) !=
- class_loader->GetClass()) {
- *result = nullptr;
- return false;
+ if (IsPathOrDexClassLoader(soa, class_loader)) {
+ // For regular path or dex class loader the search order is:
+ // - parent
+ // - class loader dex files
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ if (!FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result)) {
+ return false; // One of the parents is not supported.
+ }
+ if (*result != nullptr) {
+ return true; // Found the class up the chain.
}
+
+ // Search the current class loader classpath.
+ *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+ return true;
}
- // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
- StackHandleScope<4> hs(self);
- Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
- bool recursive_result = FindClassInBaseDexClassLoader(soa,
- self,
- descriptor,
- hash,
- h_parent,
- result);
+ if (IsDelegateLastClassLoader(soa, class_loader)) {
+ // For delegate last, the search order is:
+ // - boot class path
+ // - class loader dex files
+ // - parent
+ *result = FindClassInBootClassLoaderClassPath(self, descriptor, hash);
+ if (*result != nullptr) {
+ return true; // The class is part of the boot class path.
+ }
- if (!recursive_result) {
- // Something wrong up the chain.
- return false;
+ *result = FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader);
+ if (*result != nullptr) {
+ return true; // Found the class in the current class loader
+ }
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ return FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result);
}
- if (*result != nullptr) {
- // Found the class up the chain.
- return true;
+ // Unsupported class loader.
+ *result = nullptr;
+ return false;
+}
+
+// Finds the class in the boot class loader.
+// If the class is found the method returns the resolved class. Otherwise it returns null.
+ObjPtr<mirror::Class> ClassLinker::FindClassInBootClassLoaderClassPath(Thread* self,
+ const char* descriptor,
+ size_t hash) {
+ ObjPtr<mirror::Class> result = nullptr;
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
+ if (pair.second != nullptr) {
+ ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, nullptr);
+ if (klass != nullptr) {
+ result = EnsureResolved(self, descriptor, klass);
+ } else {
+ result = DefineClass(self,
+ descriptor,
+ hash,
+ ScopedNullHandle<mirror::ClassLoader>(),
+ *pair.first,
+ *pair.second);
+ }
+ if (result == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ }
}
+ return result;
+}
- // Handle this step.
- // Handle as if this is the child PathClassLoader.
- // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
- // We need to get the DexPathList and loop through it.
+ObjPtr<mirror::Class> ClassLinker::FindClassInBaseDexClassLoaderClassPath(
+ ScopedObjectAccessAlreadyRunnable& soa,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader) {
+ CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader))
+ << "Unexpected class loader for descriptor " << descriptor;
+
+ Thread* self = soa.Self();
ArtField* const cookie_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
ArtField* const dex_file_field =
@@ -2489,10 +2611,11 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
+ StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
@@ -2518,19 +2641,18 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
OatDexFile::FindClassDef(*cp_dex_file, descriptor, hash);
if (dex_class_def != nullptr) {
ObjPtr<mirror::Class> klass = DefineClass(self,
- descriptor,
- hash,
- class_loader,
- *cp_dex_file,
- *dex_class_def);
+ descriptor,
+ hash,
+ class_loader,
+ *cp_dex_file,
+ *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
// TODO: Is it really right to break here, and not check the other dex files?
- return true;
+ return nullptr;
}
- *result = klass;
- return true;
+ return klass;
}
}
}
@@ -2538,9 +2660,7 @@ bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnabl
}
self->AssertNoPendingException();
}
-
- // Result is still null from the parent call, no need to set it again...
- return true;
+ return nullptr;
}
mirror::Class* ClassLinker::FindClass(Thread* self,
@@ -4064,7 +4184,10 @@ verifier::FailureKind ClassLinker::VerifyClass(
while (old_status == mirror::Class::kStatusVerifying ||
old_status == mirror::Class::kStatusVerifyingAtRuntime) {
lock.WaitIgnoringInterrupts();
- CHECK(klass->IsErroneous() || (klass->GetStatus() > old_status))
+ // WaitIgnoringInterrupts can still receive an interrupt and return early, in this
+ // case we may see the same status again. b/62912904. This is why the check is
+ // greater or equal.
+ CHECK(klass->IsErroneous() || (klass->GetStatus() >= old_status))
<< "Class '" << klass->PrettyClass()
<< "' performed an illegal verification state transition from " << old_status
<< " to " << klass->GetStatus();
@@ -8253,65 +8376,139 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_id
const DexFile* const dex_file = referrer->GetDexFile();
const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx);
- union {
- ArtField* field;
- ArtMethod* method;
- uintptr_t field_or_method;
- } target;
- uint32_t num_params;
- mirror::MethodHandle::Kind kind;
+ ArtField* target_field = nullptr;
+ ArtMethod* target_method = nullptr;
+
DexFile::MethodHandleType handle_type =
static_cast<DexFile::MethodHandleType>(mh.method_handle_type_);
switch (handle_type) {
case DexFile::MethodHandleType::kStaticPut: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kStaticGet: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kInstancePut: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kInstanceGet: {
+ target_field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeStatic: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kStatic);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInstance: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kVirtual);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeConstructor: {
+ UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeDirect: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kDirect);
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInterface: {
+ target_method = ResolveMethod<kNoICCECheckForCache>(self,
+ mh.field_or_method_idx_,
+ referrer,
+ InvokeType::kInterface);
+ break;
+ }
+ }
+
+ if (target_field != nullptr) {
+ ObjPtr<mirror::Class> target_class = target_field->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CanAccessMember(target_class, target_field->GetAccessFlags())) {
+ ThrowIllegalAccessErrorField(referring_class, target_field);
+ return nullptr;
+ }
+ } else if (target_method != nullptr) {
+ ObjPtr<mirror::Class> target_class = target_method->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CanAccessMember(target_class, target_method->GetAccessFlags())) {
+ ThrowIllegalAccessErrorMethod(referring_class, target_method);
+ return nullptr;
+ }
+ } else {
+ // Common check for resolution failure.
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+
+ // Determine the kind and number of parameters after it's safe to
+ // follow the field or method pointer.
+ mirror::MethodHandle::Kind kind;
+ uint32_t num_params;
+ switch (handle_type) {
+ case DexFile::MethodHandleType::kStaticPut: {
kind = mirror::MethodHandle::Kind::kStaticPut;
- target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
num_params = 1;
break;
}
case DexFile::MethodHandleType::kStaticGet: {
kind = mirror::MethodHandle::Kind::kStaticGet;
- target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */);
num_params = 0;
break;
}
case DexFile::MethodHandleType::kInstancePut: {
kind = mirror::MethodHandle::Kind::kInstancePut;
- target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
num_params = 2;
break;
}
case DexFile::MethodHandleType::kInstanceGet: {
kind = mirror::MethodHandle::Kind::kInstanceGet;
- target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */);
num_params = 1;
break;
}
case DexFile::MethodHandleType::kInvokeStatic: {
kind = mirror::MethodHandle::Kind::kInvokeStatic;
- target.method = ResolveMethod<kNoICCECheckForCache>(self,
- mh.field_or_method_idx_,
- referrer,
- InvokeType::kStatic);
uint32_t shorty_length;
- target.method->GetShorty(&shorty_length);
- num_params = shorty_length - 1; // Remove 1 for return value.
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length - 1; // Remove 1 for the return value.
break;
}
case DexFile::MethodHandleType::kInvokeInstance: {
kind = mirror::MethodHandle::Kind::kInvokeVirtual;
- target.method = ResolveMethod<kNoICCECheckForCache>(self,
- mh.field_or_method_idx_,
- referrer,
- InvokeType::kVirtual);
uint32_t shorty_length;
- target.method->GetShorty(&shorty_length);
- num_params = shorty_length - 1; // Remove 1 for return value.
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length; // Add 1 for the receiver, remove 1 for the return value.
break;
}
case DexFile::MethodHandleType::kInvokeConstructor: {
UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform.";
num_params = 0;
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeDirect: {
+ kind = mirror::MethodHandle::Kind::kInvokeDirect;
+ uint32_t shorty_length;
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length; // Add 1 for the receiver, remove 1 for the return value.
+ break;
+ }
+ case DexFile::MethodHandleType::kInvokeInterface: {
+ kind = mirror::MethodHandle::Kind::kInvokeInterface;
+ uint32_t shorty_length;
+ target_method->GetShorty(&shorty_length);
+ num_params = shorty_length; // Add 1 for the receiver, remove 1 for the return value.
+ break;
}
}
@@ -8328,44 +8525,51 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_id
Handle<mirror::Class> return_type;
switch (handle_type) {
case DexFile::MethodHandleType::kStaticPut: {
- method_params->Set(0, target.field->GetType<true>());
+ method_params->Set(0, target_field->GetType<true>());
return_type = hs.NewHandle(FindPrimitiveClass('V'));
break;
}
case DexFile::MethodHandleType::kStaticGet: {
- return_type = hs.NewHandle(target.field->GetType<true>());
+ return_type = hs.NewHandle(target_field->GetType<true>());
break;
}
case DexFile::MethodHandleType::kInstancePut: {
- method_params->Set(0, target.field->GetDeclaringClass());
- method_params->Set(1, target.field->GetType<true>());
+ method_params->Set(0, target_field->GetDeclaringClass());
+ method_params->Set(1, target_field->GetType<true>());
return_type = hs.NewHandle(FindPrimitiveClass('V'));
break;
}
case DexFile::MethodHandleType::kInstanceGet: {
- method_params->Set(0, target.field->GetDeclaringClass());
- return_type = hs.NewHandle(target.field->GetType<true>());
+ method_params->Set(0, target_field->GetDeclaringClass());
+ return_type = hs.NewHandle(target_field->GetType<true>());
break;
}
- case DexFile::MethodHandleType::kInvokeStatic:
- case DexFile::MethodHandleType::kInvokeInstance: {
+ case DexFile::MethodHandleType::kInvokeDirect:
+ case DexFile::MethodHandleType::kInvokeInstance:
+ case DexFile::MethodHandleType::kInvokeInterface:
+ case DexFile::MethodHandleType::kInvokeStatic: {
// TODO(oth): This will not work for varargs methods as this
// requires instantiating a Transformer. This resolution step
// would be best done in managed code rather than in the run
// time (b/35235705)
Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
- DexFileParameterIterator it(*dex_file, target.method->GetPrototype());
- for (int32_t i = 0; it.HasNext(); i++, it.Next()) {
+ DexFileParameterIterator it(*dex_file, target_method->GetPrototype());
+ int32_t index = 0;
+ if (handle_type != DexFile::MethodHandleType::kInvokeStatic) {
+ method_params->Set(index++, target_method->GetDeclaringClass());
+ }
+ while (it.HasNext()) {
const dex::TypeIndex type_idx = it.GetTypeIdx();
mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader);
if (nullptr == klass) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
- method_params->Set(i, klass);
+ method_params->Set(index++, klass);
+ it.Next();
}
- return_type = hs.NewHandle(target.method->GetReturnType(true));
+ return_type = hs.NewHandle(target_method->GetReturnType(true));
break;
}
case DexFile::MethodHandleType::kInvokeConstructor: {
@@ -8385,7 +8589,14 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_id
DCHECK(self->IsExceptionPending());
return nullptr;
}
- return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt);
+
+ uintptr_t target;
+ if (target_field != nullptr) {
+ target = reinterpret_cast<uintptr_t>(target_field);
+ } else {
+ target = reinterpret_cast<uintptr_t>(target_method);
+ }
+ return mirror::MethodHandleImpl::Create(self, target, kind, mt);
}
bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const {
@@ -8549,8 +8760,15 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
return descriptor;
}
-jobject ClassLinker::CreatePathClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader) {
+ CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_PathClassLoader) ||
+ self->GetJniEnv()->IsSameObject(loader_class,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
// SOAAlreadyRunnable is protected, and we need something to add a global reference.
// We could move the jobject to the callers, but all call-sites do this...
ScopedObjectAccessUnchecked soa(self);
@@ -8586,8 +8804,8 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
for (const DexFile* dex_file : dex_files) {
StackHandleScope<4> hs2(self);
- // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
- // oat file but we can leave it null.
+ // CreateWellKnownClassLoader is only used by gtests and compiler.
+ // Index 0 of h_long_array is supposed to be the oat file but we can leave it null.
Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
self,
kDexFileIndexStart + 1));
@@ -8622,36 +8840,44 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self,
// Set elements.
dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get());
- // Create PathClassLoader.
- Handle<mirror::Class> h_path_class_class = hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
- Handle<mirror::Object> h_path_class_loader = hs.NewHandle(
- h_path_class_class->AllocObject(self));
- DCHECK(h_path_class_loader != nullptr);
+ // Create the class loader..
+ Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
+ Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+ DCHECK(h_class_loader != nullptr);
// Set DexPathList.
ArtField* path_list_field =
jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
DCHECK(path_list_field != nullptr);
- path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get());
+ path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
ArtField* const parent_field =
mirror::Class::FindField(self,
- h_path_class_loader->GetClass(),
+ h_class_loader->GetClass(),
"parent",
"Ljava/lang/ClassLoader;");
DCHECK(parent_field != nullptr);
- ObjPtr<mirror::Object> boot_cl =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
- parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl);
+
+ ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
+ ? soa.Decode<mirror::ClassLoader>(parent_loader)
+ : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
+ parent_field->SetObject<false>(h_class_loader.Get(), parent);
// Make it a global ref and return.
ScopedLocalRef<jobject> local_ref(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get()));
+ soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
return soa.Env()->NewGlobalRef(local_ref.get());
}
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files) {
+ return CreateWellKnownClassLoader(self,
+ dex_files,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ nullptr);
+}
+
void ClassLinker::DropFindArrayClassCache() {
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
find_array_class_cache_next_victim_ = 0;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1e8125eb05..de1fefd20e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -553,8 +553,24 @@ class ClassLinker {
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
+ // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
+ // that can be used to load classes from the given dex files. The parent of the class loader
+ // will be set to `parent_loader`. If `parent_loader` is null the parent will be
+ // the boot class loader.
+ // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
+ // this method will abort.
// Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
+ jobject CreateWellKnownClassLoader(Thread* self,
+ const std::vector<const DexFile*>& dex_files,
+ jclass loader_class,
+ jobject parent_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Calls CreateWellKnownClassLoader(self,
+ // dex_files,
+ // WellKnownClasses::dalvik_system_PathClassLoader,
+ // nullptr)
jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
@@ -819,6 +835,27 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ // Finds the class in the classpath of the given class loader. It only searches the class loader
+ // dex files and does not recurse into its parent.
+ // The method checks that the provided class loader is either a PathClassLoader or a
+ // DexClassLoader.
+ // If the class is found the method returns the resolved class. Otherwise it returns null.
+ ObjPtr<mirror::Class> FindClassInBaseDexClassLoaderClassPath(
+ ScopedObjectAccessAlreadyRunnable& soa,
+ const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
+ // Finds the class in the boot class loader.
+ // If the class is found the method returns the resolved class. Otherwise it returns null.
+ ObjPtr<mirror::Class> FindClassInBootClassLoaderClassPath(Thread* self,
+ const char* descriptor,
+ size_t hash)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
// Finds a class by its descriptor, returning NULL if it isn't wasn't loaded
// by the given 'class_loader'. Uses the provided hash for the descriptor.
mirror::Class* LookupClass(Thread* self,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 684a261cca..03cc6c59c4 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1533,4 +1533,110 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
ASSERT_TRUE(method1_type.Get() != method2_type.Get());
}
+// Verify that ClassLinker's CreateWellknownClassLoader works as expected
+// by creating a chain of class loaders with various dex files.
+TEST_F(ClassLinkerTest, CreateWellKnownClassLoader) {
+ // LoadDexIn*ClassLoader methods already assert that the parent loader is the expected one.
+ // No need to check again.
+ jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("Nested", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("MultiDex", class_loader_b);
+ LoadDexInDelegateLastClassLoader("Interfaces", class_loader_c);
+}
+
+class ClassLinkerClassLoaderTest : public ClassLinkerTest {
+ protected:
+ // Verifies that the class identified by the given descriptor is loaded with
+ // the expected_class_loader_obj when search from class_loader_to_search_obj.
+ // When expected_class_loader_obj is null the check will be done against BootClassLoader.
+ void VerifyClassResolution(const std::string& descriptor,
+ jobject class_loader_to_search_obj,
+ jobject expected_class_loader_obj,
+ bool should_find = true) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<3> hs(self);
+ Handle<mirror::ClassLoader> class_loader_to_search(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_to_search_obj)));
+
+ Handle<mirror::Class> klass = hs.NewHandle(
+ class_linker_->FindClass(soa.Self(), descriptor.c_str(), class_loader_to_search));
+
+ if (!should_find) {
+ if (self->IsExceptionPending()) {
+ self->ClearException();
+ }
+ ASSERT_TRUE(klass == nullptr);
+ } else if (expected_class_loader_obj == nullptr) {
+ ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+ } else {
+ ASSERT_TRUE(klass != nullptr) << descriptor;
+ Handle<mirror::ClassLoader> expected_class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(expected_class_loader_obj)));
+ ASSERT_EQ(klass->GetClassLoader(), expected_class_loader.Get());
+ }
+ }
+};
+
+TEST_F(ClassLinkerClassLoaderTest, CreatePathClassLoader) {
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+ VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+ VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateDelegateLastClassLoader) {
+ jobject class_loader_a = LoadDexInDelegateLastClassLoader("ForClassLoaderA", nullptr);
+ VerifyClassResolution("LDefinedInA;", class_loader_a, class_loader_a);
+ VerifyClassResolution("Ljava/lang/String;", class_loader_a, nullptr);
+ VerifyClassResolution("LDefinedInB;", class_loader_a, nullptr, /*should_find*/ false);
+}
+
+TEST_F(ClassLinkerClassLoaderTest, CreateClassLoaderChain) {
+ // The chain is
+ // ClassLoaderA (PathClassLoader, defines: A, AB, AC, AD)
+ // ^
+ // |
+ // ClassLoaderB (DelegateLastClassLoader, defines: B, AB, BC, BD)
+ // ^
+ // |
+ // ClassLoaderC (PathClassLoader, defines: C, AC, BC, CD)
+ // ^
+ // |
+ // ClassLoaderD (DelegateLastClassLoader, defines: D, AD, BD, CD)
+
+ jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+ jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+ jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+ jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+ // Verify exclusive classes (present in only one class loader).
+ VerifyClassResolution("LDefinedInD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInC;", class_loader_d, class_loader_c);
+ VerifyClassResolution("LDefinedInB;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInA;", class_loader_d, class_loader_a);
+
+ // Verify classes that are defined in multiple classloader.
+
+ // Classes defined in B should be found in B even if they are defined in A or C because
+ // B is a DelegateLastClassLoader.
+ VerifyClassResolution("LDefinedInAB;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInABC;", class_loader_d, class_loader_b);
+ VerifyClassResolution("LDefinedInBC;", class_loader_d, class_loader_b);
+
+ // Classes defined in D should be found in D even if they are defined in parent class loaders
+ // as well because D is a DelegateLastClassLoader.
+ VerifyClassResolution("LDefinedInAD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInBD;", class_loader_d, class_loader_d);
+ VerifyClassResolution("LDefinedInCD;", class_loader_d, class_loader_d);
+
+
+ // Classes not defined in the DelegateLastClassLoaders (i.e. D or B) should be found
+ // in the top parent.
+ VerifyClassResolution("LDefinedInAC;", class_loader_d, class_loader_a);
+
+ // Sanity check that we don't find an undefined class.
+ VerifyClassResolution("LNotDefined;", class_loader_d, nullptr, /*should_find*/ false);
+}
+
} // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 6441a44e6e..659c7e4950 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -680,19 +680,66 @@ jobject CommonRuntimeTestImpl::LoadMultiDex(const char* first_dex_name,
}
jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) {
- std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name);
+ jobject class_loader = LoadDexInPathClassLoader(dex_name, nullptr);
+ Thread::Current()->SetClassLoaderOverride(class_loader);
+ return class_loader;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
std::vector<const DexFile*> class_path;
CHECK_NE(0U, dex_files.size());
for (auto& dex_file : dex_files) {
class_path.push_back(dex_file.get());
loaded_dex_files_.push_back(std::move(dex_file));
}
-
Thread* self = Thread::Current();
- jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self,
- class_path);
- self->SetClassLoaderOverride(class_loader);
- return class_loader;
+ ScopedObjectAccess soa(self);
+
+ jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+ self,
+ class_path,
+ loader_class,
+ parent_loader);
+
+ {
+ // Verify we build the correct chain.
+
+ ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
+ // Verify that the result has the correct class.
+ CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+ // Verify that the parent is not null. The boot class loader will be set up as a
+ // proper object.
+ ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+ CHECK(actual_parent != nullptr);
+
+ if (parent_loader != nullptr) {
+ // We were given a parent. Verify that it's what we expect.
+ ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
+ CHECK_EQ(expected_parent, actual_parent);
+ } else {
+ // No parent given. The parent must be the BootClassLoader.
+ CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+ }
+ }
+
+ return result;
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_PathClassLoader,
+ parent_loader);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
+ jobject parent_loader) {
+ return LoadDexInWellKnownClassLoader(dex_name,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ parent_loader);
}
std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 3b3e6c5321..5893573bdd 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -134,10 +134,20 @@ class CommonRuntimeTestImpl {
std::unique_ptr<const DexFile> OpenTestDexFile(const char* name);
+ // Loads the test dex file identified by the given dex_name into a PathClassLoader.
+ // Returns the created class loader.
jobject LoadDex(const char* dex_name) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Loads the test dex file identified by the given first_dex_name and second_dex_name
+ // into a PathClassLoader. Returns the created class loader.
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ jobject LoadDexInPathClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
+ jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+ jclass loader_class,
+ jobject parent_loader);
+
std::string android_data_;
std::string dalvik_cache_;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 12bdb32fec..b59511214d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -18,7 +18,9 @@
#include <sys/uio.h>
+#include <memory>
#include <set>
+#include <vector>
#include "android-base/stringprintf.h"
@@ -26,6 +28,7 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/strlcpy.h"
#include "base/time_utils.h"
#include "class_linker.h"
#include "class_linker-inl.h"
@@ -77,25 +80,10 @@ static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
return alloc_record_count;
}
-// Takes a method and returns a 'canonical' one if the method is default (and therefore potentially
-// copied from some other class). This ensures that the debugger does not get confused as to which
-// method we are in.
-static ArtMethod* GetCanonicalMethod(ArtMethod* m)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (LIKELY(!m->IsDefault())) {
- return m;
- } else {
- mirror::Class* declaring_class = m->GetDeclaringClass();
- return declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
- m->GetDexMethodIndex(),
- kRuntimePointerSize);
- }
-}
-
class Breakpoint : public ValueObject {
public:
Breakpoint(ArtMethod* method, uint32_t dex_pc, DeoptimizationRequest::Kind deoptimization_kind)
- : method_(GetCanonicalMethod(method)),
+ : method_(method->GetCanonicalMethod(kRuntimePointerSize)),
dex_pc_(dex_pc),
deoptimization_kind_(deoptimization_kind) {
CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing ||
@@ -125,7 +113,7 @@ class Breakpoint : public ValueObject {
// Returns true if the method of this breakpoint and the passed in method should be considered the
// same. That is, they are either the same method or they are copied from the same method.
bool IsInMethod(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_) {
- return method_ == GetCanonicalMethod(m);
+ return method_ == m->GetCanonicalMethod(kRuntimePointerSize);
}
private:
@@ -1367,7 +1355,8 @@ JDWP::FieldId Dbg::ToFieldId(const ArtField* f) {
static JDWP::MethodId ToMethodId(ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(GetCanonicalMethod(m)));
+ return static_cast<JDWP::MethodId>(
+ reinterpret_cast<uintptr_t>(m->GetCanonicalMethod(kRuntimePointerSize)));
}
static ArtField* FromFieldId(JDWP::FieldId fid)
@@ -2460,7 +2449,7 @@ JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspen
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Thread* thread = thread_list->SuspendThreadByPeer(peer.get(),
request_suspension,
- /* debug_suspension */ true,
+ SuspendReason::kForDebugger,
&timed_out);
if (thread != nullptr) {
return JDWP::ERR_NONE;
@@ -2491,7 +2480,7 @@ void Dbg::ResumeThread(JDWP::ObjectId thread_id) {
needs_resume = thread->GetDebugSuspendCount() > 0;
}
if (needs_resume) {
- Runtime::Current()->GetThreadList()->Resume(thread, true);
+ Runtime::Current()->GetThreadList()->Resume(thread, SuspendReason::kForDebugger);
}
}
@@ -2887,7 +2876,7 @@ static void SetEventLocation(JDWP::EventLocation* location, ArtMethod* m, uint32
if (m == nullptr) {
memset(location, 0, sizeof(*location));
} else {
- location->method = GetCanonicalMethod(m);
+ location->method = m->GetCanonicalMethod(kRuntimePointerSize);
location->dex_pc = (m->IsNative() || m->IsProxyMethod()) ? static_cast<uint32_t>(-1) : dex_pc;
}
}
@@ -3708,7 +3697,7 @@ class ScopedDebuggerThreadSuspension {
ThreadList* const thread_list = Runtime::Current()->GetThreadList();
suspended_thread = thread_list->SuspendThreadByPeer(thread_peer,
/* request_suspension */ true,
- /* debug_suspension */ true,
+ SuspendReason::kForDebugger,
&timed_out);
}
if (suspended_thread == nullptr) {
@@ -3732,7 +3721,7 @@ class ScopedDebuggerThreadSuspension {
~ScopedDebuggerThreadSuspension() {
if (other_suspend_) {
- Runtime::Current()->GetThreadList()->Resume(thread_, true);
+ Runtime::Current()->GetThreadList()->Resume(thread_, SuspendReason::kForDebugger);
}
}
@@ -4054,7 +4043,7 @@ JDWP::JdwpError Dbg::PrepareInvokeMethod(uint32_t request_id, JDWP::ObjectId thr
thread_list->UndoDebuggerSuspensions();
} else {
VLOG(jdwp) << " Resuming event thread only";
- thread_list->Resume(targetThread, true);
+ thread_list->Resume(targetThread, SuspendReason::kForDebugger);
}
return JDWP::ERR_NONE;
@@ -4940,11 +4929,20 @@ class StringTable {
StringTable() {
}
- void Add(const std::string& str) {
- table_.insert(str);
- }
+ void Add(const char* str, bool copy_string) {
+ if (UNLIKELY(copy_string)) {
+ // Check whether it's already there.
+ if (table_.find(str) != table_.end()) {
+ return;
+ }
- void Add(const char* str) {
+ // Make a copy.
+ size_t str_len = strlen(str);
+ char* copy = new char[str_len + 1];
+ strlcpy(copy, str, str_len + 1);
+ string_backup_.emplace_back(copy);
+ str = copy;
+ }
table_.insert(str);
}
@@ -4961,17 +4959,23 @@ class StringTable {
}
void WriteTo(std::vector<uint8_t>& bytes) const {
- for (const std::string& str : table_) {
- const char* s = str.c_str();
- size_t s_len = CountModifiedUtf8Chars(s);
+ for (const char* str : table_) {
+ size_t s_len = CountModifiedUtf8Chars(str);
std::unique_ptr<uint16_t[]> s_utf16(new uint16_t[s_len]);
- ConvertModifiedUtf8ToUtf16(s_utf16.get(), s);
+ ConvertModifiedUtf8ToUtf16(s_utf16.get(), str);
JDWP::AppendUtf16BE(bytes, s_utf16.get(), s_len);
}
}
private:
- std::set<std::string> table_;
+ struct ConstCharStarComparator {
+ bool operator()(const char *s1, const char *s2) const {
+ return strcmp(s1, s2) < 0;
+ }
+ };
+
+ std::set<const char*, ConstCharStarComparator> table_;
+ std::vector<std::unique_ptr<char[]>> string_backup_;
DISALLOW_COPY_AND_ASSIGN(StringTable);
};
@@ -5058,12 +5062,13 @@ jbyteArray Dbg::GetRecentAllocations() {
count > 0 && it != end; count--, it++) {
const gc::AllocRecord* record = &it->second;
std::string temp;
- class_names.Add(record->GetClassDescriptor(&temp));
+ const char* class_descr = record->GetClassDescriptor(&temp);
+ class_names.Add(class_descr, !temp.empty());
for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
ArtMethod* m = record->StackElement(i).GetMethod();
- class_names.Add(m->GetDeclaringClassDescriptor());
- method_names.Add(m->GetName());
- filenames.Add(GetMethodSourceFile(m));
+ class_names.Add(m->GetDeclaringClassDescriptor(), false);
+ method_names.Add(m->GetName(), false);
+ filenames.Add(GetMethodSourceFile(m), false);
}
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 81a39afbee..eb3b210cd1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -272,7 +272,9 @@ class DexFile {
// can be any non-static method on any class (or interface) except
// for “<init>”.
kInvokeConstructor = 0x0006, // an invoker for a given constructor.
- kLast = kInvokeConstructor
+ kInvokeDirect = 0x0007, // an invoker for a direct (special) method.
+ kInvokeInterface = 0x0008, // an invoker for an interface method.
+ kLast = kInvokeInterface
};
// raw method_handle_item
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index c18ab47739..c5c4eda98f 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -2483,7 +2483,9 @@ bool DexFileVerifier::CheckInterMethodHandleItem() {
}
case DexFile::MethodHandleType::kInvokeStatic:
case DexFile::MethodHandleType::kInvokeInstance:
- case DexFile::MethodHandleType::kInvokeConstructor: {
+ case DexFile::MethodHandleType::kInvokeConstructor:
+ case DexFile::MethodHandleType::kInvokeDirect:
+ case DexFile::MethodHandleType::kInvokeInterface: {
LOAD_METHOD(method, index, "method_handle_item method_idx", return false);
break;
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 37734e8afb..6547299853 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -376,6 +376,7 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx,
mirror::Class* referring_class = referrer->GetDeclaringClass();
if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
resolved_field,
+ referrer->GetDexCache(),
field_idx))) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
@@ -461,9 +462,11 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
} else if (access_check) {
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
bool can_access_resolved_method =
- referrer->GetDeclaringClass()->CheckResolvedMethodAccess<type>(methods_class,
- resolved_method,
- method_idx);
+ referrer->GetDeclaringClass()->CheckResolvedMethodAccess(methods_class,
+ resolved_method,
+ referrer->GetDexCache(),
+ method_idx,
+ type);
if (UNLIKELY(!can_access_resolved_method)) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
@@ -662,7 +665,7 @@ inline ArtField* FindFieldFast(uint32_t field_idx, ArtMethod* referrer, FindFiel
return nullptr;
}
}
- mirror::Class* referring_class = referrer->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
if (UNLIKELY(!referring_class->CanAccess(fields_class) ||
!referring_class->CanAccessMember(fields_class, resolved_field->GetAccessFlags()) ||
(is_set && resolved_field->IsFinal() && (fields_class != referring_class)))) {
@@ -677,18 +680,17 @@ inline ArtField* FindFieldFast(uint32_t field_idx, ArtMethod* referrer, FindFiel
}
// Fast path method resolution that can't throw exceptions.
+template <InvokeType type, bool access_check>
inline ArtMethod* FindMethodFast(uint32_t method_idx,
ObjPtr<mirror::Object> this_object,
- ArtMethod* referrer,
- bool access_check,
- InvokeType type) {
+ ArtMethod* referrer) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
if (UNLIKELY(this_object == nullptr && type != kStatic)) {
return nullptr;
}
- mirror::Class* referring_class = referrer->GetDeclaringClass();
- ArtMethod* resolved_method =
- referrer->GetDexCache()->GetResolvedMethod(method_idx, kRuntimePointerSize);
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+ ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize);
if (UNLIKELY(resolved_method == nullptr)) {
return nullptr;
}
@@ -698,7 +700,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx,
if (UNLIKELY(icce)) {
return nullptr;
}
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
+ ObjPtr<mirror::Class> methods_class = resolved_method->GetDeclaringClass();
if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
!referring_class->CanAccessMember(methods_class,
resolved_method->GetAccessFlags()))) {
@@ -713,7 +715,6 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx,
return resolved_method;
} else if (type == kSuper) {
// TODO This lookup is rather slow.
- ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType(
method_type_idx, dex_cache, referrer->GetClassLoader());
@@ -727,7 +728,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx,
if (!method_reference_class->IsAssignableFrom(referring_class)) {
return nullptr;
}
- mirror::Class* super_class = referring_class->GetSuperClass();
+ ObjPtr<mirror::Class> super_class = referring_class->GetSuperClass();
if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
// The super class does not have the method.
return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index eed08aabad..fe85887f05 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -137,11 +137,10 @@ inline ArtField* FindFieldFast(uint32_t field_idx,
REQUIRES_SHARED(Locks::mutator_lock_);
// Fast path method resolution that can't throw exceptions.
+template <InvokeType type, bool access_check>
inline ArtMethod* FindMethodFast(uint32_t method_idx,
ObjPtr<mirror::Object> this_object,
- ArtMethod* referrer,
- bool access_check,
- InvokeType type)
+ ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_);
inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx,
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 2c99aeba88..36885d8a1f 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2363,7 +2363,7 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self,
// It is valid to use this, as at the usage points here (returns from C functions) we are assuming
// to hold the mutator lock (see REQUIRES_SHARED(Locks::mutator_lock_) annotations).
-template<InvokeType type, bool access_check>
+template <InvokeType type, bool access_check>
static TwoWordReturn artInvokeCommon(uint32_t method_idx,
ObjPtr<mirror::Object> this_object,
Thread* self,
@@ -2371,7 +2371,7 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx,
ScopedQuickEntrypointChecks sqec(self);
DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
- ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check, type);
+ ArtMethod* method = FindMethodFast<type, access_check>(method_idx, this_object, caller_method);
if (UNLIKELY(method == nullptr)) {
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
uint32_t shorty_len;
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 060f12db33..bf5cf29f13 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -154,8 +154,13 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
}
pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
- new_num_bytes_allocated = static_cast<size_t>(
- num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated)) + bytes_tl_bulk_allocated;
+ new_num_bytes_allocated = num_bytes_allocated_.FetchAndAddRelaxed(bytes_tl_bulk_allocated) +
+ bytes_tl_bulk_allocated;
+ if (bytes_tl_bulk_allocated > 0) {
+ // Only trace when we get an increase in the number of bytes allocated. This happens when
+ // obtaining a new TLAB and isn't often enough to hurt performance according to golem.
+ TraceHeapSize(new_num_bytes_allocated + bytes_tl_bulk_allocated);
+ }
}
if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
CHECK_LE(obj->SizeOf(), usable_size);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 880b2d40bd..ad4c0d5b2d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2678,6 +2678,10 @@ collector::GarbageCollector* Heap::Compact(space::ContinuousMemMapAllocSpace* ta
}
}
+void Heap::TraceHeapSize(size_t heap_size) {
+ ATRACE_INT("Heap size (KB)", heap_size / KB);
+}
+
collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
GcCause gc_cause,
bool clear_soft_references) {
@@ -2726,8 +2730,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
++self->GetStats()->gc_for_alloc_count;
}
const uint64_t bytes_allocated_before_gc = GetBytesAllocated();
- // Approximate heap size.
- ATRACE_INT("Heap size (KB)", bytes_allocated_before_gc / KB);
if (gc_type == NonStickyGcType()) {
// Move all bytes from new_native_bytes_allocated_ to
@@ -3632,6 +3634,8 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran,
// We know what our utilization is at this moment.
// This doesn't actually resize any memory. It just lets the heap grow more when necessary.
const uint64_t bytes_allocated = GetBytesAllocated();
+ // Trace the new heap size after the GC is finished.
+ TraceHeapSize(bytes_allocated);
uint64_t target_size;
collector::GcType gc_type = collector_ran->GetGcType();
const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 3484e0297d..9e55081b63 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1089,6 +1089,8 @@ class Heap {
return growth_limit_ / 2;
}
+ void TraceHeapSize(size_t heap_size);
+
// All-known continuous spaces, where objects lie within fixed bounds.
std::vector<space::ContinuousSpace*> continuous_spaces_ GUARDED_BY(Locks::mutator_lock_);
diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc
index 8ea0459c89..40ee86ce79 100644
--- a/runtime/gc/heap_verification_test.cc
+++ b/runtime/gc/heap_verification_test.cc
@@ -54,6 +54,11 @@ TEST_F(VerificationTest, IsValidHeapObjectAddress) {
Handle<mirror::String> string(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
EXPECT_TRUE(v->IsValidHeapObjectAddress(string.Get()));
+ // Address in the heap that isn't aligned.
+ const void* unaligned_address =
+ reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(string.Get()) + 1);
+ EXPECT_TRUE(v->IsAddressInHeapSpace(unaligned_address));
+ EXPECT_FALSE(v->IsValidHeapObjectAddress(unaligned_address));
EXPECT_TRUE(v->IsValidHeapObjectAddress(string->GetClass()));
const uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
// Not actually a valid object but the verification can't know that. Guaranteed to be inside a
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index fc24fc2974..2e67f34648 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -48,58 +48,34 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by
mirror::Object* obj;
if (LIKELY(num_bytes <= kRegionSize)) {
// Non-large object.
- if (!kForEvac) {
- obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- } else {
- DCHECK(evac_region_ != nullptr);
- obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- }
+ obj = (kForEvac ? evac_region_ : current_region_)->Alloc(num_bytes,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
MutexLock mu(Thread::Current(), region_lock_);
// Retry with current region since another thread may have updated it.
- if (!kForEvac) {
- obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- } else {
- obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- }
+ obj = (kForEvac ? evac_region_ : current_region_)->Alloc(num_bytes,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
- if (!kForEvac) {
- // Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
- return nullptr;
- }
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- r->SetNewlyAllocated();
- ++num_non_free_regions_;
- obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
- CHECK(obj != nullptr);
- current_region_ = r;
- return obj;
- }
- }
- } else {
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- ++num_non_free_regions_;
- obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
- CHECK(obj != nullptr);
- evac_region_ = r;
- return obj;
- }
+ Region* r = AllocateRegion(kForEvac);
+ if (LIKELY(r != nullptr)) {
+ obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
+ CHECK(obj != nullptr);
+ // Do our allocation before setting the region, this makes sure no threads race ahead
+ // and fill in the region before we allocate the object. b/63153464
+ if (kForEvac) {
+ evac_region_ = r;
+ } else {
+ current_region_ = r;
}
+ return obj;
}
} else {
// Large object.
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 8d8c4885ef..b8f1e8fc71 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -29,6 +29,10 @@ namespace space {
// value of the region size, evaculate the region.
static constexpr uint kEvaculateLivePercentThreshold = 75U;
+// If we protect the cleared regions.
+// Only protect for target builds to prevent flaky test failures (b/63131961).
+static constexpr bool kProtectClearedRegions = kIsTargetBuild;
+
MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity,
uint8_t* requested_begin) {
CHECK_ALIGNED(capacity, kRegionSize);
@@ -247,6 +251,13 @@ void RegionSpace::SetFromSpace(accounting::ReadBarrierTable* rb_table, bool forc
evac_region_ = &full_region_;
}
+static void ZeroAndProtectRegion(uint8_t* begin, uint8_t* end) {
+ ZeroAndReleasePages(begin, end - begin);
+ if (kProtectClearedRegions) {
+ mprotect(begin, end - begin, PROT_NONE);
+ }
+}
+
void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_objects) {
DCHECK(cleared_bytes != nullptr);
DCHECK(cleared_objects != nullptr);
@@ -265,7 +276,7 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje
auto clear_region = [&clear_block_begin, &clear_block_end](Region* r) {
r->Clear(/*zero_and_release_pages*/false);
if (clear_block_end != r->Begin()) {
- ZeroAndReleasePages(clear_block_begin, clear_block_end - clear_block_begin);
+ ZeroAndProtectRegion(clear_block_begin, clear_block_end);
clear_block_begin = r->Begin();
}
clear_block_end = r->End();
@@ -449,21 +460,14 @@ bool RegionSpace::AllocNewTlab(Thread* self, size_t min_bytes) {
MutexLock mu(self, region_lock_);
RevokeThreadLocalBuffersLocked(self);
// Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
- return false;
- }
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- ++num_non_free_regions_;
- r->SetNewlyAllocated();
- r->SetTop(r->End());
- r->is_a_tlab_ = true;
- r->thread_ = self;
- self->SetTlab(r->Begin(), r->Begin() + min_bytes, r->End());
- return true;
- }
+
+ Region* r = AllocateRegion(/*for_evac*/ false);
+ if (r != nullptr) {
+ r->is_a_tlab_ = true;
+ r->thread_ = self;
+ r->SetTop(r->End());
+ self->SetTlab(r->Begin(), r->Begin() + min_bytes, r->End());
+ return true;
}
return false;
}
@@ -543,6 +547,65 @@ size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable
return num_bytes;
}
+void RegionSpace::Region::Clear(bool zero_and_release_pages) {
+ top_.StoreRelaxed(begin_);
+ state_ = RegionState::kRegionStateFree;
+ type_ = RegionType::kRegionTypeNone;
+ objects_allocated_.StoreRelaxed(0);
+ alloc_time_ = 0;
+ live_bytes_ = static_cast<size_t>(-1);
+ if (zero_and_release_pages) {
+ ZeroAndProtectRegion(begin_, end_);
+ }
+ is_newly_allocated_ = false;
+ is_a_tlab_ = false;
+ thread_ = nullptr;
+}
+
+RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) {
+ if (!for_evac && (num_non_free_regions_ + 1) * 2 > num_regions_) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = &regions_[i];
+ if (r->IsFree()) {
+ r->Unfree(this, time_);
+ ++num_non_free_regions_;
+ if (!for_evac) {
+ // Evac doesn't count as newly allocated.
+ r->SetNewlyAllocated();
+ }
+ return r;
+ }
+ }
+ return nullptr;
+}
+
+void RegionSpace::Region::MarkAsAllocated(RegionSpace* region_space, uint32_t alloc_time) {
+ DCHECK(IsFree());
+ alloc_time_ = alloc_time;
+ region_space->AdjustNonFreeRegionLimit(idx_);
+ type_ = RegionType::kRegionTypeToSpace;
+ if (kProtectClearedRegions) {
+ mprotect(Begin(), kRegionSize, PROT_READ | PROT_WRITE);
+ }
+}
+
+void RegionSpace::Region::Unfree(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateAllocated;
+}
+
+void RegionSpace::Region::UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateLarge;
+}
+
+void RegionSpace::Region::UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateLargeTail;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 323ccdbd74..8907b07bf2 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -284,20 +284,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
return type_;
}
- void Clear(bool zero_and_release_pages) {
- top_.StoreRelaxed(begin_);
- state_ = RegionState::kRegionStateFree;
- type_ = RegionType::kRegionTypeNone;
- objects_allocated_.StoreRelaxed(0);
- alloc_time_ = 0;
- live_bytes_ = static_cast<size_t>(-1);
- if (zero_and_release_pages) {
- ZeroAndReleasePages(begin_, end_ - begin_);
- }
- is_newly_allocated_ = false;
- is_a_tlab_ = false;
- thread_ = nullptr;
- }
+ void Clear(bool zero_and_release_pages);
ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size,
@@ -315,31 +302,16 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
// Given a free region, declare it non-free (allocated).
void Unfree(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateAllocated;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
void UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateLarge;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
void UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateLargeTail;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
+
+ void MarkAsAllocated(RegionSpace* region_space, uint32_t alloc_time)
+ REQUIRES(region_space->region_lock_);
void SetNewlyAllocated() {
is_newly_allocated_ = true;
@@ -539,6 +511,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
}
}
+ Region* AllocateRegion(bool for_evac) REQUIRES(region_lock_);
+
Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
uint32_t time_; // The time as the number of collections since the startup.
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index 03b26a0a6b..beb43dfcf5 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -26,6 +26,28 @@
namespace art {
namespace gc {
+std::string Verification::DumpRAMAroundAddress(uintptr_t addr, uintptr_t bytes) const {
+ const uintptr_t dump_start = addr - bytes;
+ const uintptr_t dump_end = addr + bytes;
+ std::ostringstream oss;
+ if (dump_start < dump_end &&
+ IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_start)) &&
+ IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_end - 1))) {
+ oss << " adjacent_ram=";
+ for (uintptr_t p = dump_start; p < dump_end; ++p) {
+ if (p == addr) {
+ // Marker of where the address is.
+ oss << "|";
+ }
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(p);
+ oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr);
+ }
+ } else {
+ oss << " <invalid address>";
+ }
+ return oss.str();
+}
+
std::string Verification::DumpObjectInfo(const void* addr, const char* tag) const {
std::ostringstream oss;
oss << tag << "=" << addr;
@@ -51,23 +73,7 @@ std::string Verification::DumpObjectInfo(const void* addr, const char* tag) cons
card_table->GetCard(reinterpret_cast<const mirror::Object*>(addr)));
}
// Dump adjacent RAM.
- const uintptr_t uint_addr = reinterpret_cast<uintptr_t>(addr);
- static constexpr size_t kBytesBeforeAfter = 2 * kObjectAlignment;
- const uintptr_t dump_start = uint_addr - kBytesBeforeAfter;
- const uintptr_t dump_end = uint_addr + kBytesBeforeAfter;
- if (dump_start < dump_end &&
- IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_start)) &&
- IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_end - kObjectAlignment))) {
- oss << " adjacent_ram=";
- for (uintptr_t p = dump_start; p < dump_end; ++p) {
- if (p == uint_addr) {
- // Marker of where the object is.
- oss << "|";
- }
- uint8_t* ptr = reinterpret_cast<uint8_t*>(p);
- oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr);
- }
- }
+ oss << DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(addr), 4 * kObjectAlignment);
} else {
oss << " <invalid address>";
}
@@ -91,12 +97,15 @@ void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder,
if (holder != nullptr) {
mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>();
if (IsValidClass(holder_klass)) {
- oss << "field_offset=" << offset.Uint32Value();
+ oss << " field_offset=" << offset.Uint32Value();
ArtField* field = holder->FindFieldByOffset(offset);
if (field != nullptr) {
oss << " name=" << field->GetName();
}
}
+ mirror::HeapReference<mirror::Object>* addr = holder->GetFieldObjectReferenceAddr(offset);
+ oss << " reference addr"
+ << DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(addr), 4 * kObjectAlignment);
}
if (fatal) {
@@ -106,10 +115,7 @@ void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder,
}
}
-bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const {
- if (!IsAligned<kObjectAlignment>(addr)) {
- return false;
- }
+bool Verification::IsAddressInHeapSpace(const void* addr, space::Space** out_space) const {
space::Space* const space = heap_->FindSpaceFromAddress(addr);
if (space != nullptr) {
if (out_space != nullptr) {
@@ -120,6 +126,10 @@ bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out
return false;
}
+bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const {
+ return IsAligned<kObjectAlignment>(addr) && IsAddressInHeapSpace(addr, out_space);
+}
+
bool Verification::IsValidClass(const void* addr) const {
if (!IsValidHeapObjectAddress(addr)) {
return false;
diff --git a/runtime/gc/verification.h b/runtime/gc/verification.h
index 903e159c5a..6b456fd349 100644
--- a/runtime/gc/verification.h
+++ b/runtime/gc/verification.h
@@ -49,11 +49,10 @@ class Verification {
mirror::Object* ref,
bool fatal) const REQUIRES_SHARED(Locks::mutator_lock_);
-
// Return true if the klass is likely to be a valid mirror::Class.
bool IsValidClass(const void* klass) const REQUIRES_SHARED(Locks::mutator_lock_);
- // Does not allow null.
+ // Does not allow null, checks alignment.
bool IsValidHeapObjectAddress(const void* addr, space::Space** out_space = nullptr) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -62,6 +61,14 @@ class Verification {
std::string FirstPathFromRootSet(ObjPtr<mirror::Object> target) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Does not check alignment, used by DumpRAMAroundAddress.
+ bool IsAddressInHeapSpace(const void* addr, space::Space** out_space = nullptr) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Dump bytes of RAM before and after an address.
+ std::string DumpRAMAroundAddress(uintptr_t addr, uintptr_t bytes) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
gc::Heap* const heap_;
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 1b36c3f12b..0687b753d8 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -640,7 +640,7 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
const DexFile* dex_file = referrer->GetDexFile();
const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx);
- StackHandleScope<9> hs(self);
+ StackHandleScope<10> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
@@ -836,9 +836,13 @@ static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self,
return nullptr;
}
- // Check the target method type matches the method type requested.
- if (UNLIKELY(!target->GetMethodType()->IsExactMatch(method_type.Get()))) {
- ThrowWrongMethodTypeException(target->GetMethodType(), method_type.Get());
+ // Check the target method type matches the method type requested modulo the receiver
+ // needs to be compatible rather than exact.
+ Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType());
+ if (UNLIKELY(!target_method_type->IsExactMatch(method_type.Get()) &&
+ !IsParameterTypeConvertible(target_method_type->GetPTypes()->GetWithoutChecks(0),
+ method_type->GetPTypes()->GetWithoutChecks(0)))) {
+ ThrowWrongMethodTypeException(target_method_type.Get(), method_type.Get());
return nullptr;
}
@@ -947,13 +951,20 @@ static inline bool DoCallCommon(ArtMethod* called_method,
// Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
// PerformCall. A deoptimization could occur at any time, and we shouldn't change which
// entrypoint to use once we start building the shadow frame.
- bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
- called_method, called_method->GetEntryPointFromQuickCompiledCode());
+
+ // For unstarted runtimes, always use the interpreter entrypoint. This fixes the case where we are
+ // doing cross compilation. Note that GetEntryPointFromQuickCompiledCode doesn't use the image
+ // pointer size here and this may case an overflow if it is called from the compiler. b/62402160
+ const bool use_interpreter_entrypoint = !Runtime::Current()->IsStarted() ||
+ ClassLinker::ShouldUseInterpreterEntrypoint(
+ called_method,
+ called_method->GetEntryPointFromQuickCompiledCode());
if (LIKELY(code_item != nullptr)) {
// When transitioning to compiled code, space only needs to be reserved for the input registers.
// The rest of the frame gets discarded. This also prevents accessing the called method's code
// item, saving memory by keeping code items of compiled code untouched.
- if (Runtime::Current()->IsStarted() && !use_interpreter_entrypoint) {
+ if (!use_interpreter_entrypoint) {
+ DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
num_regs = number_of_inputs;
} else {
num_regs = code_item->registers_size_;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 45788e7617..0a2705d5f7 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -64,13 +64,22 @@ namespace interpreter {
}
// Code to run before each dex instruction.
-#define PREAMBLE() \
- do { \
- if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
- instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \
- shadow_frame.GetMethod(), dex_pc); \
+#define PREAMBLE_SAVE(save_ref) \
+ { \
+ if (UNLIKELY(instrumentation->HasDexPcListeners()) && \
+ UNLIKELY(!DoDexPcMoveEvent(self, \
+ code_item, \
+ shadow_frame, \
+ dex_pc, \
+ instrumentation, \
+ save_ref))) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ break; \
} \
- } while (false)
+ } \
+ do {} while (false)
+
+#define PREAMBLE() PREAMBLE_SAVE(nullptr)
#define BRANCH_INSTRUMENTATION(offset) \
do { \
@@ -104,6 +113,43 @@ namespace interpreter {
} \
} while (false)
+// Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if
+// the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able
+// to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by
+// jvmti-agents while handling breakpoint or single step events. We had to move this into its own
+// function because it was making ExecuteSwitchImpl have too large a stack.
+NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
+ const DexFile::CodeItem* code_item,
+ const ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ const instrumentation::Instrumentation* instrumentation,
+ JValue* save_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(instrumentation->HasDexPcListeners());
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException()));
+ mirror::Object* null_obj = nullptr;
+ HandleWrapper<mirror::Object> h(
+ hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot()));
+ self->ClearException();
+ instrumentation->DexPcMovedEvent(self,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ dex_pc);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ // We got a new exception in the dex-pc-moved event. We just let this exception replace the old
+ // one.
+ // TODO It would be good to add the old exception to the suppressed exceptions of the new one if
+ // possible.
+ return false;
+ } else {
+ if (UNLIKELY(!thr.IsNull())) {
+ self->SetException(thr.Get());
+ }
+ return true;
+ }
+}
+
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
@@ -198,7 +244,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
inst = inst->Next_1xx();
break;
case Instruction::MOVE_RESULT_OBJECT:
- PREAMBLE();
+ PREAMBLE_SAVE(&result_register);
shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
inst = inst->Next_1xx();
break;
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index b41bc78170..10dddaefc8 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -43,6 +43,33 @@ namespace art {
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
+// At what priority to schedule the saver threads. 9 is the lowest foreground priority on device.
+static constexpr int kProfileSaverPthreadPriority = 9;
+
+static void SetProfileSaverThreadPriority(pthread_t thread, int priority) {
+#if defined(ART_TARGET_ANDROID)
+ int result = setpriority(PRIO_PROCESS, pthread_gettid_np(thread), priority);
+ if (result != 0) {
+ LOG(ERROR) << "Failed to setpriority to :" << priority;
+ }
+#else
+ UNUSED(thread);
+ UNUSED(priority);
+#endif
+}
+
+static int GetDefaultThreadPriority() {
+#if defined(ART_TARGET_ANDROID)
+ pthread_attr_t attr;
+ sched_param param;
+ pthread_attr_init(&attr);
+ pthread_attr_getschedparam(&attr, &param);
+ return param.sched_priority;
+#else
+ return 0;
+#endif
+}
+
ProfileSaver::ProfileSaver(const ProfileSaverOptions& options,
const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -241,6 +268,20 @@ class GetClassesAndMethodsVisitor : public ClassVisitor {
const bool profile_boot_class_path_;
};
+class ScopedDefaultPriority {
+ public:
+ explicit ScopedDefaultPriority(pthread_t thread) : thread_(thread) {
+ SetProfileSaverThreadPriority(thread_, GetDefaultThreadPriority());
+ }
+
+ ~ScopedDefaultPriority() {
+ SetProfileSaverThreadPriority(thread_, kProfileSaverPthreadPriority);
+ }
+
+ private:
+ const pthread_t thread_;
+};
+
void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
ScopedTrace trace(__PRETTY_FUNCTION__);
const uint64_t start_time = NanoTime();
@@ -257,7 +298,15 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
TypeReferenceCollection resolved_classes(allocator.Adapter(), allocator.Adapter());
const bool is_low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
const size_t hot_threshold = options_.GetHotStartupMethodSamples(is_low_ram);
+ pthread_t profiler_pthread;
{
+ MutexLock mu(self, *Locks::profiler_lock_);
+ profiler_pthread = profiler_pthread_;
+ }
+ {
+ // Restore profile saver thread priority during the GC critical section. This helps prevent
+ // priority inversions blocking the GC for long periods of time.
+ ScopedDefaultPriority sdp(profiler_pthread);
ScopedObjectAccess soa(self);
gc::ScopedGCCriticalSection sgcs(self,
gc::kGcCauseProfileSaver,
@@ -543,15 +592,7 @@ void ProfileSaver::Start(const ProfileSaverOptions& options,
(&profiler_pthread_, nullptr, &RunProfileSaverThread, reinterpret_cast<void*>(instance_)),
"Profile saver thread");
-#if defined(ART_TARGET_ANDROID)
- // At what priority to schedule the saver threads. 9 is the lowest foreground priority on device.
- static constexpr int kProfileSaverPthreadPriority = 9;
- int result = setpriority(
- PRIO_PROCESS, pthread_gettid_np(profiler_pthread_), kProfileSaverPthreadPriority);
- if (result != 0) {
- PLOG(ERROR) << "Failed to setpriority to :" << kProfileSaverPthreadPriority;
- }
-#endif
+ SetProfileSaverThreadPriority(profiler_pthread_, kProfileSaverPthreadPriority);
}
void ProfileSaver::Stop(bool dump_info) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 12baf387d2..419a4db0fc 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -99,7 +99,7 @@ inline DexCache* Class::GetDexCache() {
inline uint32_t Class::GetCopiedMethodsStartOffset() {
// Object::GetFieldShort returns an int16_t value, but
// Class::copied_methods_offset_ is an uint16_t value; cast the
- // latter to int16_t before returning it as an uint32_t value, so
+ // latter to uint16_t before returning it as an uint32_t value, so
// that uint16_t values between 2^15 and 2^16-1 are correctly
// handled.
return static_cast<uint16_t>(
@@ -113,7 +113,7 @@ inline uint32_t Class::GetDirectMethodsStartOffset() {
inline uint32_t Class::GetVirtualMethodsStartOffset() {
// Object::GetFieldShort returns an int16_t value, but
// Class::virtual_method_offset_ is an uint16_t value; cast the
- // latter to int16_t before returning it as an uint32_t value, so
+ // latter to uint16_t before returning it as an uint32_t value, so
// that uint16_t values between 2^15 and 2^16-1 are correctly
// handled.
return static_cast<uint16_t>(
@@ -410,25 +410,24 @@ inline bool Class::IsAssignableFromArray(ObjPtr<Class> src) {
return IsArrayAssignableFromArray(src);
}
-template <bool throw_on_failure, bool use_referrers_cache>
+template <bool throw_on_failure>
inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to,
ArtField* field,
- uint32_t field_idx,
- ObjPtr<DexCache> dex_cache) {
- DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
+ ObjPtr<DexCache> dex_cache,
+ uint32_t field_idx) {
+ DCHECK(dex_cache != nullptr);
if (UNLIKELY(!this->CanAccess(access_to))) {
// The referrer class can't access the field's declaring class but may still be able
// to access the field if the FieldId specifies an accessible subclass of the declaring
// class rather than the declaring class itself.
- ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
- dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
+ dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetFieldId(field_idx).class_idx_;
// The referenced class has already been resolved with the field, but may not be in the dex
// cache. Use LookupResolveType here to search the class table if it is not in the dex cache.
// should be no thread suspension due to the class being resolved.
ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- *referrer_dex_cache->GetDexFile(),
+ *dex_cache->GetDexFile(),
class_idx,
- referrer_dex_cache,
+ dex_cache,
access_to->GetClassLoader());
DCHECK(dex_access_to != nullptr);
if (UNLIKELY(!this->CanAccess(dex_access_to))) {
@@ -447,25 +446,25 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr<Class> access_to,
return false;
}
-template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
+template <bool throw_on_failure>
inline bool Class::ResolvedMethodAccessTest(ObjPtr<Class> access_to,
ArtMethod* method,
+ ObjPtr<DexCache> dex_cache,
uint32_t method_idx,
- ObjPtr<DexCache> dex_cache) {
- static_assert(throw_on_failure || throw_invoke_type == kStatic, "Non-default throw invoke type");
- DCHECK_EQ(use_referrers_cache, dex_cache == nullptr);
+ InvokeType throw_invoke_type) {
+ DCHECK(throw_on_failure || throw_invoke_type == kStatic);
+ DCHECK(dex_cache != nullptr);
if (UNLIKELY(!this->CanAccess(access_to))) {
// The referrer class can't access the method's declaring class but may still be able
// to access the method if the MethodId specifies an accessible subclass of the declaring
// class rather than the declaring class itself.
- ObjPtr<DexCache> referrer_dex_cache = use_referrers_cache ? this->GetDexCache() : dex_cache;
- dex::TypeIndex class_idx = referrer_dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+ dex::TypeIndex class_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
// The referenced class has already been resolved with the method, but may not be in the dex
// cache.
ObjPtr<Class> dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- *referrer_dex_cache->GetDexFile(),
+ *dex_cache->GetDexFile(),
class_idx,
- referrer_dex_cache,
+ dex_cache,
access_to->GetClassLoader());
DCHECK(dex_access_to != nullptr);
if (UNLIKELY(!this->CanAccess(dex_access_to))) {
@@ -491,30 +490,30 @@ inline bool Class::CanAccessResolvedField(ObjPtr<Class> access_to,
ArtField* field,
ObjPtr<DexCache> dex_cache,
uint32_t field_idx) {
- return ResolvedFieldAccessTest<false, false>(access_to, field, field_idx, dex_cache);
+ return ResolvedFieldAccessTest<false>(access_to, field, dex_cache, field_idx);
}
inline bool Class::CheckResolvedFieldAccess(ObjPtr<Class> access_to,
ArtField* field,
+ ObjPtr<DexCache> dex_cache,
uint32_t field_idx) {
- return ResolvedFieldAccessTest<true, true>(access_to, field, field_idx, nullptr);
+ return ResolvedFieldAccessTest<true>(access_to, field, dex_cache, field_idx);
}
inline bool Class::CanAccessResolvedMethod(ObjPtr<Class> access_to,
ArtMethod* method,
ObjPtr<DexCache> dex_cache,
uint32_t method_idx) {
- return ResolvedMethodAccessTest<false, false, kStatic>(access_to, method, method_idx, dex_cache);
+ return ResolvedMethodAccessTest<false>(access_to, method, dex_cache, method_idx, kStatic);
}
-template <InvokeType throw_invoke_type>
inline bool Class::CheckResolvedMethodAccess(ObjPtr<Class> access_to,
ArtMethod* method,
- uint32_t method_idx) {
- return ResolvedMethodAccessTest<true, true, throw_invoke_type>(access_to,
- method,
- method_idx,
- nullptr);
+ ObjPtr<DexCache> dex_cache,
+ uint32_t method_idx,
+ InvokeType throw_invoke_type) {
+ return ResolvedMethodAccessTest<true>(
+ access_to, method, dex_cache, method_idx, throw_invoke_type);
}
inline bool Class::IsSubClass(ObjPtr<Class> klass) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 61d6e05416..00498bc30a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -643,7 +643,10 @@ class MANAGED Class FINAL : public Object {
ObjPtr<DexCache> dex_cache,
uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool CheckResolvedFieldAccess(ObjPtr<Class> access_to, ArtField* field, uint32_t field_idx)
+ bool CheckResolvedFieldAccess(ObjPtr<Class> access_to,
+ ArtField* field,
+ ObjPtr<DexCache> dex_cache,
+ uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
// Can this class access a resolved method?
@@ -654,10 +657,11 @@ class MANAGED Class FINAL : public Object {
ObjPtr<DexCache> dex_cache,
uint32_t method_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <InvokeType throw_invoke_type>
bool CheckResolvedMethodAccess(ObjPtr<Class> access_to,
ArtMethod* resolved_method,
- uint32_t method_idx)
+ ObjPtr<DexCache> dex_cache,
+ uint32_t method_idx,
+ InvokeType throw_invoke_type)
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsSubClass(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1352,18 +1356,19 @@ class MANAGED Class FINAL : public Object {
uint32_t end_offset)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool throw_on_failure, bool use_referrers_cache>
+ template <bool throw_on_failure>
bool ResolvedFieldAccessTest(ObjPtr<Class> access_to,
ArtField* field,
- uint32_t field_idx,
- ObjPtr<DexCache> dex_cache)
+ ObjPtr<DexCache> dex_cache,
+ uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool throw_on_failure, bool use_referrers_cache, InvokeType throw_invoke_type>
+ template <bool throw_on_failure>
bool ResolvedMethodAccessTest(ObjPtr<Class> access_to,
ArtMethod* resolved_method,
+ ObjPtr<DexCache> dex_cache,
uint32_t method_idx,
- ObjPtr<DexCache> dex_cache)
+ InvokeType throw_invoke_type)
REQUIRES_SHARED(Locks::mutator_lock_);
bool Implements(ObjPtr<Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index a110ed7f6b..5b1ba8d010 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -106,12 +106,12 @@ TEST_F(DexCacheTest, TestResolvedFieldAccess) {
EXPECT_NE(klass1->NumStaticFields(), 0u);
for (ArtField& field : klass2->GetSFields()) {
- EXPECT_FALSE((
- klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false,
- /*use_referrers_cache*/ false>(klass2.Get(),
- &field,
- field.GetDexFieldIndex(),
- klass1->GetDexCache())));
+ EXPECT_FALSE(
+ klass1->ResolvedFieldAccessTest</*throw_on_failure*/ false>(
+ klass2.Get(),
+ &field,
+ klass1->GetDexCache(),
+ field.GetDexFieldIndex()));
}
}
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 940afc8448..3e3eaae13a 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -898,7 +898,9 @@ void Monitor::InflateThinLocked(Thread* self, Handle<mirror::Object> obj, LockWo
Thread* owner;
{
ScopedThreadSuspension sts(self, kBlocked);
- owner = thread_list->SuspendThreadByThreadId(owner_thread_id, false, &timed_out);
+ owner = thread_list->SuspendThreadByThreadId(owner_thread_id,
+ SuspendReason::kInternal,
+ &timed_out);
}
if (owner != nullptr) {
// We succeeded in suspending the thread, check the lock's status didn't change.
@@ -908,7 +910,7 @@ void Monitor::InflateThinLocked(Thread* self, Handle<mirror::Object> obj, LockWo
// Go ahead and inflate the lock.
Inflate(self, owner, obj.Get(), hash_code);
}
- thread_list->Resume(owner, false);
+ thread_list->Resume(owner, SuspendReason::kInternal);
}
self->SetMonitorEnterObject(nullptr);
}
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index e86e64ed6a..7d2d0e5bb9 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -51,7 +51,10 @@ static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject p
ScopedThreadSuspension sts(soa.Self(), kNative);
ThreadList* thread_list = Runtime::Current()->GetThreadList();
bool timed_out;
- Thread* thread = thread_list->SuspendThreadByPeer(peer, true, false, &timed_out);
+ Thread* thread = thread_list->SuspendThreadByPeer(peer,
+ /* request_suspension */ true,
+ SuspendReason::kInternal,
+ &timed_out);
if (thread != nullptr) {
// Must be runnable to create returned array.
{
@@ -59,7 +62,7 @@ static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject p
trace = thread->CreateInternalStackTrace<false>(soa);
}
// Restart suspended thread.
- thread_list->Resume(thread, false);
+ thread_list->Resume(thread, SuspendReason::kInternal);
} else if (timed_out) {
LOG(ERROR) << "Trying to get thread's stack failed as the thread failed to suspend within a "
"generous timeout.";
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index e4d1705d28..8b76327fa8 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -146,13 +146,16 @@ static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
bool timed_out;
// Take suspend thread lock to avoid races with threads trying to suspend this one.
- Thread* thread = thread_list->SuspendThreadByPeer(peer, true, false, &timed_out);
+ Thread* thread = thread_list->SuspendThreadByPeer(peer,
+ /* request_suspension */ true,
+ SuspendReason::kInternal,
+ &timed_out);
if (thread != nullptr) {
{
ScopedObjectAccess soa(env);
thread->SetThreadName(name.c_str());
}
- thread_list->Resume(thread, false);
+ thread_list->Resume(thread, SuspendReason::kInternal);
} else if (timed_out) {
LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
"failed to suspend within a generous timeout.";
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 0a254aca54..c516b66d93 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -66,7 +66,9 @@ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint th
}
// Suspend thread to build stack trace.
- Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out);
+ Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id,
+ SuspendReason::kInternal,
+ &timed_out);
if (thread != nullptr) {
{
ScopedObjectAccess soa(env);
@@ -74,7 +76,7 @@ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint th
trace = Thread::InternalStackTraceToStackTraceElementArray(soa, internal_trace);
}
// Restart suspended thread.
- thread_list->Resume(thread, false);
+ thread_list->Resume(thread, SuspendReason::kInternal);
} else {
if (timed_out) {
LOG(ERROR) << "Trying to get thread's stack by id failed as the thread failed to suspend "
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index 0b93b079d5..4560dda4dd 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -422,14 +422,17 @@ JNIEXPORT void JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring jav
// Take suspend thread lock to avoid races with threads trying to suspend this one.
art::Thread* thread;
{
- thread = thread_list->SuspendThreadByPeer(jthread, true, false, &timed_out);
+ thread = thread_list->SuspendThreadByPeer(jthread,
+ true,
+ art::SuspendReason::kInternal,
+ &timed_out);
}
if (thread != NULL) {
{
art::ScopedObjectAccess soa(env);
thread->SetThreadName(name.c_str());
}
- thread_list->Resume(thread, false);
+ thread_list->Resume(thread, art::SuspendReason::kInternal);
} else if (timed_out) {
LOG(ERROR) << "Trying to set thread name to '" << name.c_str() << "' failed as the thread "
"failed to suspend within a generous timeout.";
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index e38f265c5a..aec1bd0f37 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,8 @@ cc_defaults {
"fixed_up_dex_file.cc",
"object_tagging.cc",
"OpenjdkJvmTi.cc",
+ "ti_allocator.cc",
+ "ti_breakpoint.cc",
"ti_class.cc",
"ti_class_definition.cc",
"ti_class_loader.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 0896210f1c..d3e8798bd6 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -48,6 +48,8 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_allocator.h"
+#include "ti_breakpoint.h"
#include "ti_class.h"
#include "ti_dump.h"
#include "ti_field.h"
@@ -108,22 +110,12 @@ class JvmtiFunctions {
static jvmtiError Allocate(jvmtiEnv* env, jlong size, unsigned char** mem_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_NON_NULL(mem_ptr);
- if (size < 0) {
- return ERR(ILLEGAL_ARGUMENT);
- } else if (size == 0) {
- *mem_ptr = nullptr;
- return OK;
- }
- *mem_ptr = static_cast<unsigned char*>(malloc(size));
- return (*mem_ptr != nullptr) ? OK : ERR(OUT_OF_MEMORY);
+ return AllocUtil::Allocate(env, size, mem_ptr);
}
static jvmtiError Deallocate(jvmtiEnv* env, unsigned char* mem) {
ENSURE_VALID_ENV(env);
- if (mem != nullptr) {
- free(mem);
- }
- return OK;
+ return AllocUtil::Deallocate(env, mem);
}
static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr) {
@@ -619,20 +611,17 @@ class JvmtiFunctions {
return ERR(NOT_IMPLEMENTED);
}
- static jvmtiError SetBreakpoint(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jlocation location ATTRIBUTE_UNUSED) {
+
+ static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
- return ERR(NOT_IMPLEMENTED);
+ return BreakpointUtil::SetBreakpoint(env, method, location);
}
- static jvmtiError ClearBreakpoint(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jlocation location ATTRIBUTE_UNUSED) {
+ static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
- return ERR(NOT_IMPLEMENTED);
+ return BreakpointUtil::ClearBreakpoint(env, method, location);
}
static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
@@ -924,12 +913,12 @@ class JvmtiFunctions {
}
static jvmtiError GetBytecodes(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jint* bytecode_count_ptr ATTRIBUTE_UNUSED,
- unsigned char** bytecodes_ptr ATTRIBUTE_UNUSED) {
+ jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_get_bytecodes);
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetBytecodes(env, method, bytecode_count_ptr, bytecodes_ptr);
}
static jvmtiError IsMethodNative(jvmtiEnv* env, jmethodID method, jboolean* is_native_ptr) {
@@ -1219,6 +1208,23 @@ class JvmtiFunctions {
return error;
}
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState),
+ "com.android.art.alloc.get_global_jvmti_allocation_state",
+ "Returns the total amount of memory currently allocated by all jvmtiEnvs through the"
+ " 'Allocate' jvmti function. This does not include any memory that has been deallocated"
+ " through the 'Deallocate' function. This number is approximate and might not correspond"
+ " exactly to the sum of the sizes of all not freed allocations.",
+ 1,
+ { // NOLINT [whitespace/braces] [4]
+ { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false},
+ },
+ 1,
+ { ERR(NULL_POINTER) });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index b5f12191e6..c63e50252b 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -34,6 +34,7 @@
#include <memory>
#include <type_traits>
+#include <unordered_map>
#include <unordered_set>
#include <jni.h>
@@ -46,10 +47,12 @@
#include "java_vm_ext.h"
#include "jni_env_ext.h"
#include "jvmti.h"
+#include "ti_breakpoint.h"
namespace art {
class ArtField;
-}
+class ArtMethod;
+} // namespace art
namespace openjdkjvmti {
@@ -76,6 +79,9 @@ struct ArtJvmTiEnv : public jvmtiEnv {
std::unordered_set<art::ArtField*> access_watched_fields;
std::unordered_set<art::ArtField*> modify_watched_fields;
+ // Set of breakpoints is unique to each jvmtiEnv.
+ std::unordered_set<Breakpoint> breakpoints;
+
ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
@@ -210,7 +216,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_tag_objects = 1,
.can_generate_field_modification_events = 1,
.can_generate_field_access_events = 1,
- .can_get_bytecodes = 0,
+ .can_get_bytecodes = 1,
.can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
@@ -223,10 +229,10 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_source_debug_extension = 1,
.can_access_local_variables = 0,
.can_maintain_original_method_order = 0,
- .can_generate_single_step_events = 0,
+ .can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
.can_generate_frame_pop_events = 0,
- .can_generate_breakpoint_events = 0,
+ .can_generate_breakpoint_events = 1,
.can_suspend = 0,
.can_redefine_any_class = 0,
.can_get_current_thread_cpu_time = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index af99233f90..f30d7cecb3 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -22,6 +22,7 @@
#include "events.h"
#include "jni_internal.h"
#include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
#include "art_jvmti.h"
@@ -217,6 +218,32 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A
}
}
+// Need to give custom specializations for Breakpoint since it needs to filter out which particular
+// methods/dex_pcs agents get notified on.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID jmethod,
+ jlocation location) const {
+ art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod);
+ for (ArtJvmTiEnv* env : envs) {
+ // Search for a breakpoint on this particular method and location.
+ if (env != nullptr &&
+ ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
+ env->breakpoints.find({method, location}) != env->breakpoints.end()) {
+ // We temporarily clear any pending exceptions so the event can call back into java code.
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env);
+ (*callback)(env, jnienv, jni_thread, jmethod, location);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
// Need to give custom specializations for FieldAccess and FieldModification since they need to
// filter out which particular fields agents want to get notified on.
// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 989b9af591..f749daa918 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -423,14 +423,30 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat
}
}
- // Call-back for when the dex pc moves in a method. We don't currently have any events associated
- // with this.
- void DexPcMoved(art::Thread* self ATTRIBUTE_UNUSED,
+ // Call-back for when the dex pc moves in a method.
+ void DexPcMoved(art::Thread* self,
art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
- art::ArtMethod* method ATTRIBUTE_UNUSED,
- uint32_t new_dex_pc ATTRIBUTE_UNUSED)
+ art::ArtMethod* method,
+ uint32_t new_dex_pc)
REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
- return;
+ DCHECK(!method->IsRuntimeMethod());
+ // Default methods might be copied to multiple classes. We need to get the canonical version of
+ // this method so that we can check for breakpoints correctly.
+ // TODO We should maybe do this on other events to ensure that we are consistent WRT default
+ // methods. This could interact with obsolete methods if we ever let interface redefinition
+ // happen though.
+ method = method->GetCanonicalMethod();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ jmethodID jmethod = art::jni::EncodeArtMethod(method);
+ jlocation location = static_cast<jlocation>(new_dex_pc);
+ // Step event is reported first according to the spec.
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kSingleStep)) {
+ RunEventCallback<ArtJvmtiEvent::kSingleStep>(self, jnienv, jmethod, location);
+ }
+ // Next we do the Breakpoint events. The Dispatch code will filter the individual
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kBreakpoint)) {
+ RunEventCallback<ArtJvmtiEvent::kBreakpoint>(self, jnienv, jmethod, location);
+ }
}
// Call-back for when we read from a field.
@@ -563,6 +579,9 @@ static uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) {
return art::instrumentation::Instrumentation::kFieldWritten;
case ArtJvmtiEvent::kFieldAccess:
return art::instrumentation::Instrumentation::kFieldRead;
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep:
+ return art::instrumentation::Instrumentation::kDexPcMoved;
default:
LOG(FATAL) << "Unknown event ";
return 0;
@@ -580,6 +599,8 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener,
art::gc::kCollectorTypeInstrumentation);
art::ScopedSuspendAll ssa("jvmti method tracing installation");
if (enable) {
+ // TODO Depending on the features being used we should be able to avoid deoptimizing everything
+ // like we do here.
if (!instr->AreAllMethodsDeoptimized()) {
instr->EnableMethodTracing("jvmti-tracing", /*needs_interpreter*/true);
}
@@ -601,6 +622,17 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
return;
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep: {
+ ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep
+ : ArtJvmtiEvent::kBreakpoint;
+ // We only need to do anything if there isn't already a listener installed/held-on by the
+ // other jvmti event that uses DexPcMoved.
+ if (!IsEventEnabledAnywhere(other)) {
+ SetupTraceListener(method_trace_listener_.get(), event, enable);
+ }
+ return;
+ }
case ArtJvmtiEvent::kMethodEntry:
case ArtJvmtiEvent::kMethodExit:
case ArtJvmtiEvent::kFieldAccess:
diff --git a/runtime/openjdkjvmti/jvmti_allocator.h b/runtime/openjdkjvmti/jvmti_allocator.h
index 1225c143f9..44b1cb1c20 100644
--- a/runtime/openjdkjvmti/jvmti_allocator.h
+++ b/runtime/openjdkjvmti/jvmti_allocator.h
@@ -36,6 +36,8 @@
#include "base/macros.h"
#include "jvmti.h"
+#include "ti_allocator.h"
+
namespace openjdkjvmti {
template <typename T> class JvmtiAllocator;
@@ -53,6 +55,7 @@ class JvmtiAllocator<void> {
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+ explicit JvmtiAllocator() : env_(nullptr) {}
template <typename U>
JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit
@@ -89,6 +92,7 @@ class JvmtiAllocator {
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
+ explicit JvmtiAllocator() : env_(nullptr) {}
template <typename U>
JvmtiAllocator(const JvmtiAllocator<U>& other) // NOLINT, implicit
@@ -108,8 +112,8 @@ class JvmtiAllocator {
pointer allocate(size_type n, JvmtiAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
DCHECK_LE(n, max_size());
if (env_ == nullptr) {
- T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
- CHECK(result != nullptr || n == 0u); // Abort if malloc() fails.
+ T* result = reinterpret_cast<T*>(AllocUtil::AllocateImpl(n * sizeof(T)));
+ CHECK(result != nullptr || n == 0u); // Abort if AllocateImpl() fails.
return result;
} else {
unsigned char* result;
@@ -120,7 +124,7 @@ class JvmtiAllocator {
}
void deallocate(pointer p, size_type n ATTRIBUTE_UNUSED) {
if (env_ == nullptr) {
- free(p);
+ AllocUtil::DeallocateImpl(reinterpret_cast<unsigned char*>(p));
} else {
jvmtiError dealloc_error = env_->Deallocate(reinterpret_cast<unsigned char*>(p));
CHECK(dealloc_error == JVMTI_ERROR_NONE);
diff --git a/runtime/openjdkjvmti/jvmti_weak_table.h b/runtime/openjdkjvmti/jvmti_weak_table.h
index 01c24b1917..a5175a42ba 100644
--- a/runtime/openjdkjvmti/jvmti_weak_table.h
+++ b/runtime/openjdkjvmti/jvmti_weak_table.h
@@ -40,6 +40,7 @@
#include "gc_root-inl.h"
#include "globals.h"
#include "jvmti.h"
+#include "jvmti_allocator.h"
#include "mirror/object.h"
#include "thread-current-inl.h"
@@ -191,7 +192,7 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder {
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_);
- template <typename Storage, class Allocator = std::allocator<T>>
+ template <typename Storage, class Allocator = JvmtiAllocator<T>>
struct ReleasableContainer;
struct HashGcRoot {
@@ -209,10 +210,12 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder {
}
};
+ using TagAllocator = JvmtiAllocator<std::pair<const art::GcRoot<art::mirror::Object>, T>>;
std::unordered_map<art::GcRoot<art::mirror::Object>,
T,
HashGcRoot,
- EqGcRoot> tagged_objects_
+ EqGcRoot,
+ TagAllocator> tagged_objects_
GUARDED_BY(allow_disallow_lock_)
GUARDED_BY(art::Locks::mutator_lock_);
// To avoid repeatedly scanning the whole table, remember if we did that since the last sweep.
diff --git a/runtime/openjdkjvmti/ti_allocator.cc b/runtime/openjdkjvmti/ti_allocator.cc
new file mode 100644
index 0000000000..8a0237d6c3
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_allocator.cc
@@ -0,0 +1,89 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_allocator.h"
+
+#include <malloc.h>
+#include <atomic>
+
+#include "art_jvmti.h"
+#include "base/enums.h"
+
+namespace openjdkjvmti {
+
+std::atomic<jlong> AllocUtil::allocated;
+
+jvmtiError AllocUtil::GetGlobalJvmtiAllocationState(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jlong* allocated_ptr) {
+ if (allocated_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ *allocated_ptr = allocated.load();
+ return OK;
+}
+
+jvmtiError AllocUtil::Allocate(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jlong size,
+ unsigned char** mem_ptr) {
+ if (size < 0) {
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (size == 0) {
+ *mem_ptr = nullptr;
+ return OK;
+ }
+ *mem_ptr = AllocateImpl(size);
+ if (UNLIKELY(*mem_ptr == nullptr)) {
+ return ERR(OUT_OF_MEMORY);
+ }
+ return OK;
+}
+
+unsigned char* AllocUtil::AllocateImpl(jlong size) {
+ unsigned char* ret = size != 0 ? reinterpret_cast<unsigned char*>(malloc(size)) : nullptr;
+ if (LIKELY(ret != nullptr)) {
+ allocated += malloc_usable_size(ret);
+ }
+ return ret;
+}
+
+jvmtiError AllocUtil::Deallocate(jvmtiEnv* env ATTRIBUTE_UNUSED, unsigned char* mem) {
+ DeallocateImpl(mem);
+ return OK;
+}
+
+void AllocUtil::DeallocateImpl(unsigned char* mem) {
+ if (mem != nullptr) {
+ allocated -= malloc_usable_size(mem);
+ free(mem);
+ }
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_allocator.h b/runtime/openjdkjvmti/ti_allocator.h
new file mode 100644
index 0000000000..35575c3884
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_allocator.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2016 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_ALLOCATOR_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_ALLOCATOR_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include <atomic>
+#include <memory>
+
+namespace openjdkjvmti {
+
+template<typename T>
+class JvmtiAllocator;
+
+class AllocUtil {
+ public:
+ static jvmtiError Allocate(jvmtiEnv* env, jlong size, unsigned char** mem_ptr);
+ static jvmtiError Deallocate(jvmtiEnv* env, unsigned char* mem);
+ static jvmtiError GetGlobalJvmtiAllocationState(jvmtiEnv* env, jlong* total_allocated);
+
+ private:
+ static void DeallocateImpl(unsigned char* mem);
+ static unsigned char* AllocateImpl(jlong size);
+
+ static std::atomic<jlong> allocated;
+
+ template <typename T>
+ friend class JvmtiAllocator;
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_ALLOCATOR_H_
+
diff --git a/runtime/openjdkjvmti/ti_breakpoint.cc b/runtime/openjdkjvmti/ti_breakpoint.cc
new file mode 100644
index 0000000000..6d0e2c60c1
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.cc
@@ -0,0 +1,114 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <functional>
+
+#include "ti_breakpoint.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "dex_file_annotations.h"
+#include "events-inl.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object_array-inl.h"
+#include "modifiers.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_phase.h"
+
+namespace openjdkjvmti {
+
+size_t Breakpoint::hash() const {
+ return std::hash<uintptr_t> {}(reinterpret_cast<uintptr_t>(method_))
+ ^ std::hash<jlocation> {}(location_);
+}
+
+Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_(loc) {
+ DCHECK(!m->IsDefault() || !m->IsCopied() || !m->IsInvokable())
+ << "Flags are: 0x" << std::hex << m->GetAccessFlags();
+}
+
+void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) {
+ std::vector<Breakpoint> to_remove;
+ for (const Breakpoint& b : env->breakpoints) {
+ if (b.GetMethod()->GetDeclaringClass() == klass) {
+ to_remove.push_back(b);
+ }
+ }
+ for (const Breakpoint& b : to_remove) {
+ auto it = env->breakpoints.find(b);
+ DCHECK(it != env->breakpoints.end());
+ env->breakpoints.erase(it);
+ }
+}
+
+jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ // Need to get mutator_lock_ so we can find the interface version of any default methods.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
+ if (location < 0 || static_cast<uint32_t>(location) >=
+ art_method->GetCodeItem()->insns_size_in_code_units_) {
+ return ERR(INVALID_LOCATION);
+ }
+ auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
+ if (!res_pair.second) {
+ // Didn't get inserted because it's already present!
+ return ERR(DUPLICATE);
+ }
+ return OK;
+}
+
+jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ // Need to get mutator_lock_ so we can find the interface version of any default methods.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ auto pos = env->breakpoints.find(
+ /* Breakpoint */ {art::jni::DecodeArtMethod(method)->GetCanonicalMethod(), location});
+ if (pos == env->breakpoints.end()) {
+ return ERR(NOT_FOUND);
+ }
+ env->breakpoints.erase(pos);
+ return OK;
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_breakpoint.h b/runtime/openjdkjvmti/ti_breakpoint.h
new file mode 100644
index 0000000000..c3dbef7baf
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "base/mutex.h"
+
+namespace art {
+class ArtMethod;
+namespace mirror {
+class Class;
+} // namespace mirror
+} // namespace art
+
+namespace openjdkjvmti {
+
+struct ArtJvmTiEnv;
+
+class Breakpoint {
+ public:
+ Breakpoint(art::ArtMethod* m, jlocation loc);
+
+ // Get the hash code of this breakpoint.
+ size_t hash() const;
+
+ bool operator==(const Breakpoint& other) const {
+ return method_ == other.method_ && location_ == other.location_;
+ }
+
+ art::ArtMethod* GetMethod() const {
+ return method_;
+ }
+
+ jlocation GetLocation() const {
+ return location_;
+ }
+
+ private:
+ art::ArtMethod* method_;
+ jlocation location_;
+};
+
+class BreakpointUtil {
+ public:
+ static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+ static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+ // Used by class redefinition to remove breakpoints on redefined classes.
+ static void RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass)
+ REQUIRES(art::Locks::mutator_lock_);
+};
+
+} // namespace openjdkjvmti
+
+namespace std {
+template<> struct hash<openjdkjvmti::Breakpoint> {
+ size_t operator()(const openjdkjvmti::Breakpoint& b) const {
+ return b.hash();
+ }
+};
+
+} // namespace std
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 0ac08d9cb8..b8e79555ae 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -598,6 +598,13 @@ jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env,
return ERR(INVALID_CLASS);
}
+ // Check if this class is a temporary class object used for loading. Since we are seeing it the
+ // class must not have been prepared yet since otherwise the fixup would have gotten the jobject
+ // to point to the final class object.
+ if (klass->IsTemp() || klass->IsRetired()) {
+ return ERR(CLASS_NOT_PREPARED);
+ }
+
if (field_count_ptr == nullptr || fields_ptr == nullptr) {
return ERR(NULL_POINTER);
}
@@ -639,6 +646,13 @@ jvmtiError ClassUtil::GetClassMethods(jvmtiEnv* env,
return ERR(INVALID_CLASS);
}
+ // Check if this class is a temporary class object used for loading. Since we are seeing it the
+ // class must not have been prepared yet since otherwise the fixup would have gotten the jobject
+ // to point to the final class object.
+ if (klass->IsTemp() || klass->IsRetired()) {
+ return ERR(CLASS_NOT_PREPARED);
+ }
+
if (method_count_ptr == nullptr || methods_ptr == nullptr) {
return ERR(NULL_POINTER);
}
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index beb639e208..9b5b964a4d 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -91,6 +91,40 @@ void MethodUtil::Unregister() {
runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
}
+jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* size_ptr,
+ unsigned char** bytecode_ptr) {
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+
+ if (size_ptr == nullptr || bytecode_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
+ if (code_item == nullptr) {
+ *size_ptr = 0;
+ *bytecode_ptr = nullptr;
+ return OK;
+ }
+ // 2 bytes per instruction for dex code.
+ *size_ptr = code_item->insns_size_in_code_units_ * 2;
+ jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
+ if (err != OK) {
+ return err;
+ }
+ memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+ return OK;
+}
+
jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
jmethodID method,
jint* size_ptr) {
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index cc161c8fed..d95a81b63b 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -44,6 +44,11 @@ class MethodUtil {
static void Register(EventHandler* event_handler);
static void Unregister();
+ static jvmtiError GetBytecodes(jvmtiEnv* env,
+ jmethodID method,
+ jint* count_ptr,
+ unsigned char** bytecodes);
+
static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr);
static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr);
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 5422f48664..debee913ee 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -64,6 +64,7 @@
#include "object_lock.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
#include "ti_class_loader.h"
#include "transform.h"
#include "verifier/method_verifier.h"
@@ -380,7 +381,7 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
art::jit::ScopedJitSuspend suspend_jit;
// Get shared mutator lock so we can lock all the classes.
art::ScopedObjectAccess soa(self);
- Redefiner r(runtime, self, error_msg);
+ Redefiner r(env, runtime, self, error_msg);
for (const ArtClassDefinition& def : definitions) {
// Only try to transform classes that have been modified.
if (def.IsModified()) {
@@ -1200,6 +1201,10 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
return true;
}
+void Redefiner::ClassRedefinition::UnregisterJvmtiBreakpoints() {
+ BreakpointUtil::RemoveBreakpointsInClass(driver_->env_, GetMirrorClass());
+}
+
void Redefiner::ClassRedefinition::UnregisterBreakpoints() {
DCHECK(art::Dbg::IsDebuggerActive());
art::JDWP::JdwpState* state = art::Dbg::GetJdwpState();
@@ -1342,6 +1347,7 @@ jvmtiError Redefiner::Run() {
// TODO Rewrite so we don't do a stack walk for each and every class.
redef.FindAndAllocateObsoleteMethods(klass);
redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile());
+ redef.UnregisterJvmtiBreakpoints();
}
RestoreObsoleteMethodMapsIfUnneeded(holder);
// TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index ec4a8b2789..27d7c3d726 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -199,6 +199,8 @@ class Redefiner {
void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // This should be done with all threads suspended.
+ void UnregisterJvmtiBreakpoints() REQUIRES(art::Locks::mutator_lock_);
private:
Redefiner* driver_;
@@ -208,6 +210,7 @@ class Redefiner {
art::ArrayRef<const unsigned char> original_dex_file_;
};
+ ArtJvmTiEnv* env_;
jvmtiError result_;
art::Runtime* runtime_;
art::Thread* self_;
@@ -216,10 +219,12 @@ class Redefiner {
// mirror::Class difficult and confusing.
std::string* error_msg_;
- Redefiner(art::Runtime* runtime,
+ Redefiner(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
art::Thread* self,
std::string* error_msg)
- : result_(ERR(INTERNAL)),
+ : env_(env),
+ result_(ERR(INTERNAL)),
runtime_(runtime),
self_(self),
redefinitions_(),
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 424dcf85cf..6313553255 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -20,6 +20,7 @@
#include <iostream>
+#include "base/memory_tool.h"
#include "runtime_common.h"
namespace art {
@@ -63,6 +64,16 @@ void HandleUnexpectedSignalLinux(int signal_number, siginfo_t* info, void* raw_c
}
void Runtime::InitPlatformSignalHandlers() {
+ constexpr bool kIsASAN =
+#ifdef ADDRESS_SANITIZER
+ true;
+#else
+ false;
+#endif
+ if (!kIsTargetBuild && kIsASAN) {
+ // (Temporarily) try and let ASAN print abort stacks, as our code sometimes fails. b/31098551
+ return;
+ }
// On the host, we don't have debuggerd to dump a stack for us when something unexpected happens.
InitPlatformSignalHandlersCommon(HandleUnexpectedSignalLinux,
nullptr,
diff --git a/runtime/suspend_reason.h b/runtime/suspend_reason.h
new file mode 100644
index 0000000000..27c4d3207b
--- /dev/null
+++ b/runtime/suspend_reason.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_SUSPEND_REASON_H_
+#define ART_RUNTIME_SUSPEND_REASON_H_
+
+#include <ostream>
+
+namespace art {
+
+// The various reasons that we might be suspending a thread.
+enum class SuspendReason {
+ // Suspending for internal reasons (e.g. GC, stack trace, etc.).
+ // TODO Split this into more descriptive sections.
+ kInternal,
+ // Suspending for debugger (code in Dbg::*, runtime/jdwp/, etc.).
+ kForDebugger,
+};
+
+std::ostream& operator<<(std::ostream& os, const SuspendReason& thread);
+
+} // namespace art
+
+#endif // ART_RUNTIME_SUSPEND_REASON_H_
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 7da15d9f4c..95608b5f63 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -330,12 +330,12 @@ inline void Thread::PoisonObjectPointersIfDebug() {
inline bool Thread::ModifySuspendCount(Thread* self,
int delta,
AtomicInteger* suspend_barrier,
- bool for_debugger) {
+ SuspendReason reason) {
if (delta > 0 && ((kUseReadBarrier && this != self) || suspend_barrier != nullptr)) {
// When delta > 0 (requesting a suspend), ModifySuspendCountInternal() may fail either if
// active_suspend_barriers is full or we are in the middle of a thread flip. Retry in a loop.
while (true) {
- if (LIKELY(ModifySuspendCountInternal(self, delta, suspend_barrier, for_debugger))) {
+ if (LIKELY(ModifySuspendCountInternal(self, delta, suspend_barrier, reason))) {
return true;
} else {
// Failure means the list of active_suspend_barriers is full or we are in the middle of a
@@ -354,7 +354,7 @@ inline bool Thread::ModifySuspendCount(Thread* self,
}
}
} else {
- return ModifySuspendCountInternal(self, delta, suspend_barrier, for_debugger);
+ return ModifySuspendCountInternal(self, delta, suspend_barrier, reason);
}
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index be1614b3cc..36ecd3398c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -549,27 +549,47 @@ void Thread::InstallImplicitProtection() {
//
// We map in the stack by reading every page from the stack bottom (highest address)
// to the stack top. (We then madvise this away.) This must be done by reading from the
- // current stack pointer downwards. Any access more than a page below the current SP
- // might cause a segv.
- // TODO: This comment may be out of date. It seems possible to speed this up. As
- // this is normally done once in the zygote on startup, ignore for now.
+ // current stack pointer downwards.
//
- // AddressSanitizer does not like the part of this functions that reads every stack page.
- // Looks a lot like an out-of-bounds access.
+ // Accesses too far below the current machine register corresponding to the stack pointer (e.g.,
+ // ESP on x86[-32], SP on ARM) might cause a SIGSEGV (at least on x86 with newer kernels). We
+ // thus have to move the stack pointer. We do this portably by using a recursive function with a
+ // large stack frame size.
- // (Defensively) first remove the protection on the protected region as will want to read
+ // (Defensively) first remove the protection on the protected region as we'll want to read
// and write it. Ignore errors.
UnprotectStack();
VLOG(threads) << "Need to map in stack for thread at " << std::hex <<
static_cast<void*>(pregion);
- // Read every page from the high address to the low.
- volatile uint8_t dont_optimize_this;
- UNUSED(dont_optimize_this);
- for (uint8_t* p = stack_top; p >= pregion; p -= kPageSize) {
- dont_optimize_this = *p;
- }
+ struct RecurseDownStack {
+ // This function has an intentionally large stack size.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+ NO_INLINE
+ static void Touch(uintptr_t target) {
+ volatile size_t zero = 0;
+ // Use a large local volatile array to ensure a large frame size. Do not use anything close
+ // to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but
+ // there is no pragma support for this.
+ // Note: for ASAN we need to shrink the array a bit, as there's other overhead.
+ constexpr size_t kAsanMultiplier =
+#ifdef ADDRESS_SANITIZER
+ 2u;
+#else
+ 1u;
+#endif
+ volatile char space[kPageSize - (kAsanMultiplier * 256)];
+ char sink ATTRIBUTE_UNUSED = space[zero];
+ if (reinterpret_cast<uintptr_t>(space) >= target + kPageSize) {
+ Touch(target);
+ }
+ zero *= 2; // Try to avoid tail recursion.
+ }
+#pragma GCC diagnostic pop
+ };
+ RecurseDownStack::Touch(reinterpret_cast<uintptr_t>(pregion));
VLOG(threads) << "(again) installing stack protected region at " << std::hex <<
static_cast<void*>(pregion) << " to " <<
@@ -1178,10 +1198,10 @@ static void UnsafeLogFatalForSuspendCount(Thread* self, Thread* thread) NO_THREA
bool Thread::ModifySuspendCountInternal(Thread* self,
int delta,
AtomicInteger* suspend_barrier,
- bool for_debugger) {
+ SuspendReason reason) {
if (kIsDebugBuild) {
DCHECK(delta == -1 || delta == +1 || delta == -tls32_.debug_suspend_count)
- << delta << " " << tls32_.debug_suspend_count << " " << this;
+ << reason << " " << delta << " " << tls32_.debug_suspend_count << " " << this;
DCHECK_GE(tls32_.suspend_count, tls32_.debug_suspend_count) << this;
Locks::thread_suspend_count_lock_->AssertHeld(self);
if (this != self && !IsSuspended()) {
@@ -1217,8 +1237,12 @@ bool Thread::ModifySuspendCountInternal(Thread* self,
}
tls32_.suspend_count += delta;
- if (for_debugger) {
- tls32_.debug_suspend_count += delta;
+ switch (reason) {
+ case SuspendReason::kForDebugger:
+ tls32_.debug_suspend_count += delta;
+ break;
+ case SuspendReason::kInternal:
+ break;
}
if (tls32_.suspend_count == 0) {
@@ -1458,7 +1482,7 @@ bool Thread::RequestSynchronousCheckpoint(Closure* function) {
{
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
- if (!ModifySuspendCount(self, +1, nullptr, false)) {
+ if (!ModifySuspendCount(self, +1, nullptr, SuspendReason::kInternal)) {
// Just retry the loop.
sched_yield();
continue;
@@ -1483,7 +1507,7 @@ bool Thread::RequestSynchronousCheckpoint(Closure* function) {
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
DCHECK_NE(GetState(), ThreadState::kRunnable);
- bool updated = ModifySuspendCount(self, -1, nullptr, false);
+ bool updated = ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
diff --git a/runtime/thread.h b/runtime/thread.h
index 770173e47e..e785ddc803 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -40,6 +40,7 @@
#include "managed_stack.h"
#include "offsets.h"
#include "runtime_stats.h"
+#include "suspend_reason.h"
#include "thread_state.h"
class BacktraceMap;
@@ -244,7 +245,7 @@ class Thread {
bool ModifySuspendCount(Thread* self,
int delta,
AtomicInteger* suspend_barrier,
- bool for_debugger)
+ SuspendReason reason)
WARN_UNUSED
REQUIRES(Locks::thread_suspend_count_lock_);
@@ -1300,7 +1301,7 @@ class Thread {
bool ModifySuspendCountInternal(Thread* self,
int delta,
AtomicInteger* suspend_barrier,
- bool for_debugger)
+ SuspendReason reason)
WARN_UNUSED
REQUIRES(Locks::thread_suspend_count_lock_);
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 95aba79ed7..fc767ed899 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -332,7 +332,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback
// Spurious fail, try again.
continue;
}
- bool updated = thread->ModifySuspendCount(self, +1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, +1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
suspended_count_modified_threads.push_back(thread);
break;
@@ -375,7 +375,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback
checkpoint_function->Run(thread);
{
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
}
@@ -583,7 +583,7 @@ size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor,
if ((state == kWaitingForGcThreadFlip || thread->IsTransitioningToRunnable()) &&
thread->GetSuspendCount() == 1) {
// The thread will resume right after the broadcast.
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
++runnable_thread_count;
} else {
@@ -617,7 +617,7 @@ size_t ThreadList::FlipThreadRoots(Closure* thread_flip_visitor,
TimingLogger::ScopedTiming split4("ResumeOtherThreads", collector->GetTimings());
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
for (const auto& thread : other_threads) {
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
Thread::resume_cond_->Broadcast(self);
@@ -688,7 +688,7 @@ void ThreadList::SuspendAll(const char* cause, bool long_suspend) {
void ThreadList::SuspendAllInternal(Thread* self,
Thread* ignore1,
Thread* ignore2,
- bool debug_suspend) {
+ SuspendReason reason) {
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
Locks::thread_suspend_count_lock_->AssertNotHeld(self);
@@ -718,7 +718,7 @@ void ThreadList::SuspendAllInternal(Thread* self,
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
// Update global suspend all state for attaching threads.
++suspend_all_count_;
- if (debug_suspend) {
+ if (reason == SuspendReason::kForDebugger) {
++debug_suspend_all_count_;
}
pending_threads.StoreRelaxed(list_.size() - num_ignored);
@@ -728,7 +728,7 @@ void ThreadList::SuspendAllInternal(Thread* self,
continue;
}
VLOG(threads) << "requesting thread suspend: " << *thread;
- bool updated = thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend);
+ bool updated = thread->ModifySuspendCount(self, +1, &pending_threads, reason);
DCHECK(updated);
// Must install the pending_threads counter first, then check thread->IsSuspend() and clear
@@ -807,7 +807,7 @@ void ThreadList::ResumeAll() {
if (thread == self) {
continue;
}
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
@@ -828,14 +828,13 @@ void ThreadList::ResumeAll() {
}
}
-void ThreadList::Resume(Thread* thread, bool for_debugger) {
+void ThreadList::Resume(Thread* thread, SuspendReason reason) {
// This assumes there was an ATRACE_BEGIN when we suspended the thread.
ATRACE_END();
Thread* self = Thread::Current();
DCHECK_NE(thread, self);
- VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") starting..."
- << (for_debugger ? " (debugger)" : "");
+ VLOG(threads) << "Resume(" << reinterpret_cast<void*>(thread) << ") starting..." << reason;
{
// To check Contains.
@@ -850,7 +849,7 @@ void ThreadList::Resume(Thread* thread, bool for_debugger) {
<< ") thread not within thread list";
return;
}
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, for_debugger);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, reason);
DCHECK(updated);
}
@@ -882,7 +881,7 @@ static void ThreadSuspendByPeerWarning(Thread* self,
Thread* ThreadList::SuspendThreadByPeer(jobject peer,
bool request_suspension,
- bool debug_suspension,
+ SuspendReason reason,
bool* timed_out) {
const uint64_t start_time = NanoTime();
useconds_t sleep_us = kThreadSuspendInitialSleepUs;
@@ -910,7 +909,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer,
bool updated = suspended_thread->ModifySuspendCount(soa.Self(),
-1,
nullptr,
- debug_suspension);
+ reason);
DCHECK(updated);
}
ThreadSuspendByPeerWarning(self,
@@ -937,7 +936,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer,
}
CHECK(suspended_thread == nullptr);
suspended_thread = thread;
- bool updated = suspended_thread->ModifySuspendCount(self, +1, nullptr, debug_suspension);
+ bool updated = suspended_thread->ModifySuspendCount(self, +1, nullptr, reason);
DCHECK(updated);
request_suspension = false;
} else {
@@ -973,7 +972,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer,
bool updated = suspended_thread->ModifySuspendCount(soa.Self(),
-1,
nullptr,
- debug_suspension);
+ reason);
DCHECK(updated);
}
*timed_out = true;
@@ -1002,7 +1001,7 @@ static void ThreadSuspendByThreadIdWarning(LogSeverity severity,
}
Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id,
- bool debug_suspension,
+ SuspendReason reason,
bool* timed_out) {
const uint64_t start_time = NanoTime();
useconds_t sleep_us = kThreadSuspendInitialSleepUs;
@@ -1047,7 +1046,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id,
// which will allow this thread to be suspended.
continue;
}
- bool updated = thread->ModifySuspendCount(self, +1, nullptr, debug_suspension);
+ bool updated = thread->ModifySuspendCount(self, +1, nullptr, reason);
DCHECK(updated);
suspended_thread = thread;
} else {
@@ -1079,7 +1078,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id,
"Thread suspension timed out",
thread_id);
if (suspended_thread != nullptr) {
- bool updated = thread->ModifySuspendCount(soa.Self(), -1, nullptr, debug_suspension);
+ bool updated = thread->ModifySuspendCount(soa.Self(), -1, nullptr, reason);
DCHECK(updated);
}
*timed_out = true;
@@ -1114,7 +1113,7 @@ void ThreadList::SuspendAllForDebugger() {
VLOG(threads) << *self << " SuspendAllForDebugger starting...";
- SuspendAllInternal(self, self, debug_thread, true);
+ SuspendAllInternal(self, self, debug_thread, SuspendReason::kForDebugger);
// Block on the mutator lock until all Runnable threads release their share of access then
// immediately unlock again.
#if HAVE_TIMED_RWLOCK
@@ -1157,7 +1156,7 @@ void ThreadList::SuspendSelfForDebugger() {
// to ensure that we're the only one fiddling with the suspend count
// though.
MutexLock mu(self, *Locks::thread_suspend_count_lock_);
- bool updated = self->ModifySuspendCount(self, +1, nullptr, true);
+ bool updated = self->ModifySuspendCount(self, +1, nullptr, SuspendReason::kForDebugger);
DCHECK(updated);
CHECK_GT(self->GetSuspendCount(), 0);
@@ -1242,7 +1241,7 @@ void ThreadList::ResumeAllForDebugger() {
continue;
}
VLOG(threads) << "requesting thread resume: " << *thread;
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, true);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kForDebugger);
DCHECK(updated);
}
}
@@ -1275,7 +1274,7 @@ void ThreadList::UndoDebuggerSuspensions() {
bool suspended = thread->ModifySuspendCount(self,
-thread->GetDebugSuspendCount(),
nullptr,
- true);
+ SuspendReason::kForDebugger);
DCHECK(suspended);
}
}
@@ -1333,7 +1332,7 @@ void ThreadList::SuspendAllDaemonThreadsForShutdown() {
// daemons.
CHECK(thread->IsDaemon()) << *thread;
if (thread != self) {
- bool updated = thread->ModifySuspendCount(self, +1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, +1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
++daemons_left;
}
@@ -1394,11 +1393,11 @@ void ThreadList::Register(Thread* self) {
// Modify suspend count in increments of 1 to maintain invariants in ModifySuspendCount. While
// this isn't particularly efficient the suspend counts are most commonly 0 or 1.
for (int delta = debug_suspend_all_count_; delta > 0; delta--) {
- bool updated = self->ModifySuspendCount(self, +1, nullptr, true);
+ bool updated = self->ModifySuspendCount(self, +1, nullptr, SuspendReason::kForDebugger);
DCHECK(updated);
}
for (int delta = suspend_all_count_ - debug_suspend_all_count_; delta > 0; delta--) {
- bool updated = self->ModifySuspendCount(self, +1, nullptr, false);
+ bool updated = self->ModifySuspendCount(self, +1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
CHECK(!Contains(self));
@@ -1495,12 +1494,12 @@ void ThreadList::VisitRootsForSuspendedThreads(RootVisitor* visitor) {
MutexLock mu(self, *Locks::thread_list_lock_);
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
for (Thread* thread : list_) {
- bool suspended = thread->ModifySuspendCount(self, +1, nullptr, false);
+ bool suspended = thread->ModifySuspendCount(self, +1, nullptr, SuspendReason::kInternal);
DCHECK(suspended);
if (thread == self || thread->IsSuspended()) {
threads_to_visit.push_back(thread);
} else {
- bool resumed = thread->ModifySuspendCount(self, -1, nullptr, false);
+ bool resumed = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(resumed);
}
}
@@ -1516,7 +1515,7 @@ void ThreadList::VisitRootsForSuspendedThreads(RootVisitor* visitor) {
{
MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
for (Thread* thread : threads_to_visit) {
- bool updated = thread->ModifySuspendCount(self, -1, nullptr, false);
+ bool updated = thread->ModifySuspendCount(self, -1, nullptr, SuspendReason::kInternal);
DCHECK(updated);
}
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 92702c6498..41c5e328b0 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -23,6 +23,7 @@
#include "base/time_utils.h"
#include "base/value_object.h"
#include "jni.h"
+#include "suspend_reason.h"
#include <bitset>
#include <list>
@@ -64,7 +65,7 @@ class ThreadList {
void ResumeAll()
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
UNLOCK_FUNCTION(Locks::mutator_lock_);
- void Resume(Thread* thread, bool for_debugger = false)
+ void Resume(Thread* thread, SuspendReason reason = SuspendReason::kInternal)
REQUIRES(!Locks::thread_suspend_count_lock_);
// Suspends all threads and gets exclusive access to the mutator_lock_.
@@ -81,7 +82,9 @@ class ThreadList {
// If the thread should be suspended then value of request_suspension should be true otherwise
// the routine will wait for a previous suspend request. If the suspension times out then *timeout
// is set to true.
- Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension,
+ Thread* SuspendThreadByPeer(jobject peer,
+ bool request_suspension,
+ SuspendReason reason,
bool* timed_out)
REQUIRES(!Locks::mutator_lock_,
!Locks::thread_list_lock_,
@@ -91,7 +94,7 @@ class ThreadList {
// thread on success else null. The thread id is used to identify the thread to avoid races with
// the thread terminating. Note that as thread ids are recycled this may not suspend the expected
// thread, that may be terminating. If the suspension times out then *timeout is set to true.
- Thread* SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, bool* timed_out)
+ Thread* SuspendThreadByThreadId(uint32_t thread_id, SuspendReason reason, bool* timed_out)
REQUIRES(!Locks::mutator_lock_,
!Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
@@ -198,7 +201,7 @@ class ThreadList {
void SuspendAllInternal(Thread* self,
Thread* ignore1,
Thread* ignore2 = nullptr,
- bool debug_suspend = false)
+ SuspendReason reason = SuspendReason::kInternal)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
void AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread* ignore2 = nullptr)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 9b652553df..efb02f6205 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4159,7 +4159,8 @@ bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) {
const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx);
if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx
- << " argument 0 method handle type is not InvokeStatic";
+ << " argument 0 method handle type is not InvokeStatic: "
+ << mh.method_handle_type_;
return false;
}
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 24f194b5ee..8d505e2582 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -37,6 +37,7 @@ namespace art {
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
+jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
jclass WellKnownClasses::dalvik_system_DexClassLoader;
jclass WellKnownClasses::dalvik_system_DexFile;
jclass WellKnownClasses::dalvik_system_DexPathList;
@@ -270,6 +271,7 @@ void WellKnownClasses::Init(JNIEnv* env) {
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
+ dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c18473197b..c5a16c1c76 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -47,6 +47,7 @@ struct WellKnownClasses {
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_system_BaseDexClassLoader;
+ static jclass dalvik_system_DelegateLastClassLoader;
static jclass dalvik_system_DexClassLoader;
static jclass dalvik_system_DexFile;
static jclass dalvik_system_DexPathList;
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index df1012ea3c..f3d4d77214 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -25,6 +25,8 @@
#include <vector>
#include "android-base/stringprintf.h"
+#include "ziparchive/zip_archive.h"
+
#include "base/bit_utils.h"
#include "base/unix_file/fd_file.h"
diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h
index 18584447e8..821cc5ceb1 100644
--- a/runtime/zip_archive.h
+++ b/runtime/zip_archive.h
@@ -18,7 +18,6 @@
#define ART_RUNTIME_ZIP_ARCHIVE_H_
#include <stdint.h>
-#include <ziparchive/zip_archive.h>
#include <memory>
#include <string>
@@ -29,6 +28,10 @@
#include "os.h"
#include "safe_map.h"
+// system/core/zip_archive definitions.
+struct ZipEntry;
+typedef void* ZipArchiveHandle;
+
namespace art {
class ZipArchive;
diff --git a/test/098-ddmc/src/Main.java b/test/098-ddmc/src/Main.java
index 72c5a28203..e9a11d76c2 100644
--- a/test/098-ddmc/src/Main.java
+++ b/test/098-ddmc/src/Main.java
@@ -14,8 +14,11 @@
* limitations under the License.
*/
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
public class Main {
public static void main(String[] args) throws Exception {
@@ -27,6 +30,8 @@ public class Main {
testRecentAllocationTracking();
}
+ private static ArrayList<Object> staticHolder = new ArrayList<>(100000);
+
private static void testRecentAllocationTracking() throws Exception {
System.out.println("Confirm empty");
Allocations empty = new Allocations(DdmVmInternal.getRecentAllocations());
@@ -44,18 +49,15 @@ public class Main {
System.out.println("Confirm when we overflow, we don't roll over to zero. b/17392248");
final int overflowAllocations = 64 * 1024; // Won't fit in unsigned 16-bit value.
for (int i = 0; i < overflowAllocations; i++) {
- new Object() {
- // Add a finalizer so that the allocation won't be eliminated.
- public void finalize() {
- System.out.print("");
- }
- };
+ allocate(i, 0);
}
Allocations after = new Allocations(DdmVmInternal.getRecentAllocations());
System.out.println("before < overflowAllocations=" + (before.numberOfEntries < overflowAllocations));
System.out.println("after > before=" + (after.numberOfEntries > before.numberOfEntries));
System.out.println("after.numberOfEntries=" + after.numberOfEntries);
+ staticHolder.clear(); // Free the allocated objects.
+
System.out.println("Disable and confirm back to empty");
DdmVmInternal.enableRecentAllocations(false);
System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
@@ -72,7 +74,7 @@ public class Main {
DdmVmInternal.enableRecentAllocations(true);
System.out.println("status=" + DdmVmInternal.getRecentAllocationStatus());
for (int i = 0; i < 16 * 1024; i++) {
- new String("fnord");
+ staticHolder.add(new String("fnord"));
}
Allocations first = new Allocations(DdmVmInternal.getRecentAllocations());
DdmVmInternal.enableRecentAllocations(true);
@@ -86,6 +88,50 @@ public class Main {
System.out.println("goodbye=" + goodbye);
}
+ // Allocate a simple object. Use depth for a reasonably deep stack.
+ private static final int ALLOCATE1_DEPTH = 50;
+
+ private static Object createProxy() {
+ try {
+ InvocationHandler handler = new InvocationHandler() {
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ // Don't expect to be invoked.
+ return null;
+ }
+ };
+ return Proxy.newProxyInstance(Main.class.getClassLoader(),
+ new Class[] { Runnable.class }, handler);
+ } catch (Exception e) {
+ // We don't really expect exceptions here.
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void allocate(int i, int depth) {
+ if (depth >= ALLOCATE1_DEPTH) {
+ // Mix proxies, int arrays and Objects to test the different descriptor paths.
+ switch (i) {
+ case 0:
+ staticHolder.add(createProxy());
+ break;
+
+ case 1:
+ staticHolder.add(new int[0]);
+ break;
+
+ case 2:
+ staticHolder.add(new Object[0]);
+ break;
+
+ default:
+ staticHolder.add(new Object());
+ break;
+ }
+ } else {
+ allocate(i, depth + 1);
+ }
+ }
+
private static class Allocations {
final int messageHeaderLen;
final int entryHeaderLen;
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 9072c8b538..3cfe0064fc 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -65,7 +65,8 @@ public class Main {
String line;
int count = 0;
while ((line = reader.readLine()) != null) {
- if (line.contains("@141-class-unload-ex.jar")) {
+ if (line.contains("141-class-unload-ex.odex") ||
+ line.contains("141-class-unload-ex.vdex")) {
System.out.println(line);
++count;
}
diff --git a/test/163-app-image-methods/expected.txt b/test/163-app-image-methods/expected.txt
new file mode 100644
index 0000000000..f63e8e302f
--- /dev/null
+++ b/test/163-app-image-methods/expected.txt
@@ -0,0 +1,3 @@
+Eating all memory.
+memoryWasAllocated = true
+match: true
diff --git a/test/163-app-image-methods/info.txt b/test/163-app-image-methods/info.txt
new file mode 100644
index 0000000000..7b42ebc2bb
--- /dev/null
+++ b/test/163-app-image-methods/info.txt
@@ -0,0 +1,3 @@
+Regression test for erroneously storing an ArtMethod* in the app image DexCache
+when the class from the corresponding MethodId is not in the app image, only the
+declaring class is.
diff --git a/test/163-app-image-methods/profile b/test/163-app-image-methods/profile
new file mode 100644
index 0000000000..6585b94292
--- /dev/null
+++ b/test/163-app-image-methods/profile
@@ -0,0 +1,2 @@
+LAAA/Base;
+LMain;
diff --git a/test/163-app-image-methods/run b/test/163-app-image-methods/run
new file mode 100644
index 0000000000..7cc107a1ba
--- /dev/null
+++ b/test/163-app-image-methods/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Use a profile to put specific classes in the app image.
+# Also run the compiler with -j1 to ensure specific class verification order.
+exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
+ -Xcompiler-option -j1
diff --git a/test/163-app-image-methods/src/AAA/Base.java b/test/163-app-image-methods/src/AAA/Base.java
new file mode 100644
index 0000000000..7ba71ad4fe
--- /dev/null
+++ b/test/163-app-image-methods/src/AAA/Base.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package AAA;
+
+class Base {
+ // The method is public but the class is package-private.
+ public static int foo() { return 42; }
+}
diff --git a/test/163-app-image-methods/src/AAA/Derived.java b/test/163-app-image-methods/src/AAA/Derived.java
new file mode 100644
index 0000000000..66e156fe34
--- /dev/null
+++ b/test/163-app-image-methods/src/AAA/Derived.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package AAA;
+
+public class Derived extends Base {
+ // Allows public access to Base.foo() (Base is package-private) referenced as Derived.foo().
+}
diff --git a/test/163-app-image-methods/src/Main.java b/test/163-app-image-methods/src/Main.java
new file mode 100644
index 0000000000..a995bb8412
--- /dev/null
+++ b/test/163-app-image-methods/src/Main.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AAA.Derived;
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ // Allocate memory for the "AAA.Derived" class name before eating memory.
+ String aaaDerivedName = "AAA.Derived";
+ System.out.println("Eating all memory.");
+ Object memory = eatAllMemory();
+
+ // This test assumes that Derived is not yet resolved. In some configurations
+ // (notably interp-ac), Derived is already resolved by verifying Main at run
+ // time. Therefore we cannot assume that we get a certain `value` and need to
+ // simply check for consistency, i.e. `value == another_value`.
+ int value = 0;
+ try {
+ // If the ArtMethod* is erroneously left in the DexCache, this
+ // shall succeed despite the class Derived being unresolved so
+ // far. Otherwise, we shall throw OOME trying to resolve it.
+ value = Derived.foo();
+ } catch (OutOfMemoryError e) {
+ value = -1;
+ }
+ int another_value = 0;
+ try {
+ // For comparison, try to resolve the class Derived directly.
+ Class.forName(aaaDerivedName, false, Main.class.getClassLoader());
+ another_value = 42;
+ } catch (OutOfMemoryError e) {
+ another_value = -1;
+ }
+ boolean memoryWasAllocated = (memory != null);
+ memory = null;
+ System.out.println("memoryWasAllocated = " + memoryWasAllocated);
+ System.out.println("match: " + (value == another_value));
+ if (value != another_value || (value != -1 && value != 42)) {
+ // Mismatch or unexpected value, print additional debugging information.
+ System.out.println("value: " + value);
+ System.out.println("another_value: " + another_value);
+ }
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ }
+ }
+
+ public static Object eatAllMemory() {
+ Object[] result = null;
+ int size = 1000000;
+ while (result == null && size != 0) {
+ try {
+ result = new Object[size];
+ } catch (OutOfMemoryError oome) {
+ size /= 2;
+ }
+ }
+ if (result != null) {
+ int index = 0;
+ while (index != result.length && size != 0) {
+ try {
+ result[index] = new byte[size];
+ ++index;
+ } catch (OutOfMemoryError oome) {
+ size /= 2;
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/test/1900-track-alloc/alloc.cc b/test/1900-track-alloc/alloc.cc
new file mode 100644
index 0000000000..db5617c54c
--- /dev/null
+++ b/test/1900-track-alloc/alloc.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1900TrackAlloc {
+
+typedef jvmtiError (*GetGlobalState)(jvmtiEnv* env, jlong* allocated);
+
+struct AllocTrackingData {
+ GetGlobalState get_global_state;
+};
+
+template <typename T>
+static void Dealloc(T* t) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename ...Rest>
+static void Dealloc(T* t, Rest... rs) {
+ Dealloc(t);
+ Dealloc(rs...);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1900_doDeallocate(JNIEnv* env,
+ jclass,
+ jlong jvmti_env_ptr,
+ jlong ptr) {
+ JvmtiErrorToException(env,
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->Deallocate(
+ reinterpret_cast<unsigned char*>(static_cast<intptr_t>(ptr))));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_doAllocate(JNIEnv* env,
+ jclass,
+ jlong jvmti_env_ptr,
+ jlong size) {
+ unsigned char* res = nullptr;
+ JvmtiErrorToException(env,
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr),
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->Allocate(size, &res));
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(res));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_getAmountAllocated(JNIEnv* env, jclass) {
+ AllocTrackingData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return -1;
+ }
+ if (data == nullptr || data->get_global_state == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Alloc tracking data not initialized.");
+ return -1;
+ }
+ jlong allocated = -1;
+ JvmtiErrorToException(env, jvmti_env, data->get_global_state(jvmti_env, &allocated));
+ return allocated;
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+ for (jint i = 0; i < n_params; i++) {
+ Dealloc(params[i].name);
+ }
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Test1900_getDefaultJvmtiEnv(JNIEnv*, jclass) {
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(jvmti_env));
+}
+
+extern "C" JNIEXPORT void Java_art_Test1900_destroyJvmtiEnv(JNIEnv* env,
+ jclass,
+ jlong jvmti_env_ptr) {
+ JvmtiErrorToException(env,
+ jvmti_env,
+ reinterpret_cast<jvmtiEnv*>(jvmti_env_ptr)->DisposeEnvironment());
+}
+
+extern "C" JNIEXPORT jlong Java_art_Test1900_newJvmtiEnv(JNIEnv* env, jclass) {
+ JavaVM* vm = nullptr;
+ if (env->GetJavaVM(&vm) != 0) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to get JavaVM");
+ return -1;
+ }
+ jvmtiEnv* new_env = nullptr;
+ if (vm->GetEnv(reinterpret_cast<void**>(&new_env), JVMTI_VERSION_1_0) != 0) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv");
+ return -1;
+ }
+ return static_cast<jlong>(reinterpret_cast<intptr_t>(new_env));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1900_initializeTest(JNIEnv* env, jclass) {
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ AllocTrackingData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(AllocTrackingData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(AllocTrackingData));
+ // Get the extensions.
+ jint n_ext = 0;
+ jvmtiExtensionFunctionInfo* infos = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+ return;
+ }
+ for (jint i = 0; i < n_ext; i++) {
+ jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+ if (strcmp("com.android.art.alloc.get_global_jvmti_allocation_state", cur_info->id) == 0) {
+ data->get_global_state = reinterpret_cast<GetGlobalState>(cur_info->func);
+ }
+ // Cleanup the cur_info
+ DeallocParams(cur_info->params, cur_info->param_count);
+ Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+ }
+ // Cleanup the array.
+ Dealloc(infos);
+ if (data->get_global_state == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions.");
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data));
+ return;
+}
+
+} // namespace Test1900TrackAlloc
+} // namespace art
diff --git a/test/988-redefine-use-after-free/expected.txt b/test/1900-track-alloc/expected.txt
index e69de29bb2..e69de29bb2 100644
--- a/test/988-redefine-use-after-free/expected.txt
+++ b/test/1900-track-alloc/expected.txt
diff --git a/test/1900-track-alloc/info.txt b/test/1900-track-alloc/info.txt
new file mode 100644
index 0000000000..e1d35ae026
--- /dev/null
+++ b/test/1900-track-alloc/info.txt
@@ -0,0 +1 @@
+Tests the jvmti-extension to get allocated memory snapshot.
diff --git a/test/988-redefine-use-after-free/run b/test/1900-track-alloc/run
index c6e62ae6cd..c6e62ae6cd 100755
--- a/test/988-redefine-use-after-free/run
+++ b/test/1900-track-alloc/run
diff --git a/test/1900-track-alloc/src/Main.java b/test/1900-track-alloc/src/Main.java
new file mode 100644
index 0000000000..0dab4ef726
--- /dev/null
+++ b/test/1900-track-alloc/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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) throws Exception {
+ art.Test1900.run();
+ }
+}
diff --git a/test/1900-track-alloc/src/art/Main.java b/test/1900-track-alloc/src/art/Main.java
new file mode 100644
index 0000000000..aa5498bd62
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+// Binder class so the agent's C code has something that can be bound and exposed to tests.
+// In a package to separate cleanly and work around CTS reference issues (though this class
+// should be replaced in the CTS version).
+public class Main {
+ // Load the given class with the given classloader, and bind all native methods to corresponding
+ // C methods in the agent. Will abort if any of the steps fail.
+ public static native void bindAgentJNI(String className, ClassLoader classLoader);
+ // Same as above, giving the class directly.
+ public static native void bindAgentJNIForClass(Class<?> klass);
+
+ // Common infrastructure.
+ public static native void setTag(Object o, long tag);
+ public static native long getTag(Object o);
+}
diff --git a/test/1900-track-alloc/src/art/Test1900.java b/test/1900-track-alloc/src/art/Test1900.java
new file mode 100644
index 0000000000..becee1b15c
--- /dev/null
+++ b/test/1900-track-alloc/src/art/Test1900.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+
+public class Test1900 {
+ public static void checkLE(long exp, long o) {
+ if (exp > o) {
+ throw new Error("Expected: " + exp + " Got: " + o);
+ }
+ }
+ public static void checkEq(long exp, long o) {
+ if (exp != o) {
+ throw new Error("Expected: " + exp + " Got: " + o);
+ }
+ }
+
+ public static void runConcurrent(Runnable... rs) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(rs.length);
+ Thread[] thrs = new Thread[rs.length];
+ for (int i = 0; i < rs.length; i++) {
+ final Runnable r = rs[i];
+ thrs[i] = new Thread(() -> {
+ latch.countDown();
+ r.run();
+ });
+ thrs[i].start();
+ }
+ for (Thread thr : thrs) {
+ thr.join();
+ }
+ }
+ static class Holder {
+ public long val;
+ }
+
+ public static void run() throws Exception {
+ initializeTest();
+ // Get the overhead for the native part of this test.
+ final long base_state = getAmountAllocated();
+
+ // Basic alloc-dealloc
+ checkEq(base_state + 0, getAmountAllocated());
+ long abc = doAllocate(10);
+ checkLE(base_state + 10, getAmountAllocated());
+ long def = doAllocate(10);
+ checkLE(base_state + 20, getAmountAllocated());
+ doDeallocate(abc);
+ checkLE(base_state + 10, getAmountAllocated());
+
+ doDeallocate(def);
+
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it concurrently.
+ Runnable add10 = () -> { long x = doAllocate(10); doDeallocate(x); };
+ Runnable[] rs = new Runnable[100];
+ Arrays.fill(rs, add10);
+ runConcurrent(rs);
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it concurrently with different threads to allocate and deallocate.
+ final Semaphore sem = new Semaphore(0);
+ final Holder h = new Holder();
+ runConcurrent(
+ () -> {
+ try {
+ h.val = doAllocate(100);
+ checkLE(base_state + 100, getAmountAllocated());
+ sem.release();
+ } catch (Exception e) { throw new Error("exception!", e); }
+ },
+ () -> {
+ try {
+ sem.acquire();
+ long after_acq = getAmountAllocated();
+ doDeallocate(h.val);
+ checkLE(base_state + 100, after_acq);
+ } catch (Exception e) { throw new Error("exception!", e); }
+ }
+ );
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try doing it with multiple jvmtienvs.
+ long env1 = newJvmtiEnv();
+ long env2 = newJvmtiEnv();
+
+ final long new_base_state = getAmountAllocated();
+ // new jvmtienvs shouldn't save us memory.
+ checkLE(base_state, new_base_state);
+ // Make sure we track both.
+ abc = doAllocate(env1, 10);
+ checkLE(new_base_state + 10, getAmountAllocated());
+ def = doAllocate(env2, 10);
+ checkLE(new_base_state + 20, getAmountAllocated());
+ doDeallocate(env1, abc);
+ checkLE(new_base_state + 10, getAmountAllocated());
+
+ doDeallocate(env2, def);
+
+ checkEq(new_base_state + 0, getAmountAllocated());
+
+ destroyJvmtiEnv(env1);
+ destroyJvmtiEnv(env2);
+
+ // Back to normal after getting rid of the envs.
+ checkEq(base_state + 0, getAmountAllocated());
+
+ // Try adding some tags
+ Object a = new Object();
+ Object b = new Object();
+ Main.setTag(a, 100);
+ Main.setTag(b, 200);
+
+ // tags should be counted and should have some data associated with them.
+ checkLE(base_state + 1, getAmountAllocated());
+ }
+
+ private static native long doAllocate(long jvmtienv, long size);
+ private static long doAllocate(long size) {
+ return doAllocate(getDefaultJvmtiEnv(), size);
+ }
+
+ private static native void doDeallocate(long jvmtienv, long ptr);
+ private static void doDeallocate(long size) {
+ doDeallocate(getDefaultJvmtiEnv(), size);
+ }
+
+ private static native long getDefaultJvmtiEnv();
+ private static native long newJvmtiEnv();
+ private static native void destroyJvmtiEnv(long jvmtienv);
+ private static native long getAmountAllocated();
+ private static native void initializeTest();
+}
diff --git a/test/1901-get-bytecodes/bytecodes.cc b/test/1901-get-bytecodes/bytecodes.cc
new file mode 100644
index 0000000000..edcb734788
--- /dev/null
+++ b/test/1901-get-bytecodes/bytecodes.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1901Bytecodes {
+
+extern "C" JNIEXPORT jbyteArray JNICALL Java_art_Test1901_getBytecodes(JNIEnv* env,
+ jclass,
+ jobject jmethod) {
+ jmethodID method = env->FromReflectedMethod(jmethod);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ unsigned char* bytecodes = nullptr;
+ jint bytecodes_size = 0;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetBytecodes(method, &bytecodes_size, &bytecodes))) {
+ return nullptr;
+ }
+ jbyteArray out = env->NewByteArray(bytecodes_size);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ } else if (bytecodes_size == 0) {
+ return out;
+ }
+ jbyte* bytes = env->GetByteArrayElements(out, /* is_copy */ nullptr);
+ memcpy(bytes, bytecodes, bytecodes_size);
+ env->ReleaseByteArrayElements(out, bytes, 0);
+ return out;
+}
+
+} // namespace Test1901Bytecodes
+} // namespace art
diff --git a/test/1901-get-bytecodes/expected.txt b/test/1901-get-bytecodes/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/1901-get-bytecodes/expected.txt
diff --git a/test/1901-get-bytecodes/info.txt b/test/1901-get-bytecodes/info.txt
new file mode 100644
index 0000000000..c8c91893e5
--- /dev/null
+++ b/test/1901-get-bytecodes/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/1901-get-bytecodes/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/src/Main.java b/test/1901-get-bytecodes/src/Main.java
new file mode 100644
index 0000000000..d37fcdb0cb
--- /dev/null
+++ b/test/1901-get-bytecodes/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test1901.run();
+ }
+}
diff --git a/test/1901-get-bytecodes/src/art/Test1901.java b/test/1901-get-bytecodes/src/art/Test1901.java
new file mode 100644
index 0000000000..6940491e3c
--- /dev/null
+++ b/test/1901-get-bytecodes/src/art/Test1901.java
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Base64;
+
+public class Test1901 {
+ // Class & Dex file containing the following class.
+ // Using this representation to prevent any changes to the compiler or the file formats from
+ // changing the output of this test.
+ //
+ // package art;
+ // public class Target {
+ // public void doNothing() {
+ // return;
+ // }
+ //
+ // public static void staticNothing() {
+ // return;
+ // }
+ //
+ // public void sayHi() {
+ // System.out.println("hello");
+ // }
+ // }
+ public static byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAlkb05vdGhpbmcBAA1zdGF0aWNOb3RoaW5nAQAFc2F5SGkBAApT" +
+ "b3VyY2VGaWxlAQALVGFyZ2V0LmphdmEMAAcACAcAGAwAGQAaAQAFaGVsbG8HABsMABwAHQEACmFy" +
+ "dC9UYXJnZXQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxq" +
+ "YXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExq" +
+ "YXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAAEAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGx" +
+ "AAAAAQAKAAAABgABAAAAAgABAAsACAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAABAAJ" +
+ "AAwACAABAAkAAAAZAAAAAAAAAAGxAAAAAQAKAAAABgABAAAACAABAA0ACAABAAkAAAAlAAIAAQAA" +
+ "AAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAMAAgADQABAA4AAAACAA8=");
+ public static byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAbYkxNjiZ8a+fNWF4smR2+uXbrq88/FNoYAwAAcAAAAHhWNBIAAAAAAAAAAHgCAAAP" +
+ "AAAAcAAAAAYAAACsAAAAAgAAAMQAAAABAAAA3AAAAAYAAADkAAAAAQAAABQBAADkAQAANAEAAJoB" +
+ "AACiAQAAsAEAAMcBAADbAQAA7wEAAAMCAAAQAgAAEwIAABcCAAAiAgAAKQIAAC4CAAA3AgAAPgIA" +
+ "AAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAcAAAAFAAAAAAAAAAgAAAAFAAAAlAEAAAQAAQALAAAA" +
+ "AAAAAAAAAAAAAAAACQAAAAAAAAANAAAAAAAAAA4AAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAAC" +
+ "AAAAAAAAAAYAAAAAAAAAYgIAAAAAAAABAAEAAQAAAE0CAAAEAAAAcBAFAAAADgAAAAAAAAAAAFIC" +
+ "AAABAAAADgAAAAEAAQAAAAAAVwIAAAEAAAAOAAAAAwABAAIAAABcAgAACAAAAGIAAAAaAQoAbiAE" +
+ "ABAADgABAAAAAwAGPGluaXQ+AAxMYXJ0L1RhcmdldDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+ "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+ "OwALVGFyZ2V0LmphdmEAAVYAAlZMAAlkb05vdGhpbmcABWhlbGxvAANvdXQAB3ByaW50bG4ABXNh" +
+ "eUhpAA1zdGF0aWNOb3RoaW5nAAIABw4ACAAHDgAEAAcOAAwABw54AAAAAgIAgYAEtAIDCcwCAQHg" +
+ "AgEB9AINAAAAAAAAAAEAAAAAAAAAAQAAAA8AAABwAAAAAgAAAAYAAACsAAAAAwAAAAIAAADEAAAA" +
+ "BAAAAAEAAADcAAAABQAAAAYAAADkAAAABgAAAAEAAAAUAQAAASAAAAQAAAA0AQAAARAAAAEAAACU" +
+ "AQAAAiAAAA8AAACaAQAAAyAAAAQAAABNAgAAACAAAAEAAABiAgAAABAAAAEAAAB4AgAA");
+
+ public static byte[][] DO_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for doNothing
+ // 0e00 |0000: return-void
+ new byte[] { 14, 0 },
+ // Java bytecodes
+ // 0: return
+ new byte[] { -79 },
+ };
+
+ public static byte[][] STATIC_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for staticNothing
+ // 0e00 |0000: return-void
+ new byte[] { 14, 0 },
+ // Java bytecodes
+ // 0: return
+ new byte[] { -79 },
+ };
+
+ public static byte[][] SAY_HI_NOTHING_BYTECODES = new byte[][] {
+ // Dex Bytecodes for sayHi
+ // 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+ // 1a01 0a00 |0002: const-string v1, "hello" // string@000a
+ // 6e20 0400 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0004
+ // 0e00 |0007: return-void
+ new byte[] { 98, 0, 0, 0, 26, 1, 10, 0, 110, 32, 4, 0, 16, 0, 14, 0 },
+ // Java bytecodes
+ // 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
+ // 3: ldc #3 // String hello
+ // 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
+ // 8: return
+ new byte[] { -78, 0, 2, 18, 3, -74, 0, 4, -79 },
+ };
+
+ public static ClassLoader getClassLoader() throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ // We are on art since we got the InMemoryDexClassLoader.
+ return (ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test1901.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI.
+ return new ClassLoader(Test1901.class.getClassLoader()) {
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("art.Target")) {
+ return defineClass(name, CLASS_BYTES, 0, CLASS_BYTES.length);
+ } else {
+ return super.findClass(name);
+ }
+ }
+ };
+ }
+ }
+
+ public static void CheckMethodBytes(Method m, byte[][] possible_bytecodes) {
+ byte[] real_codes = getBytecodes(m);
+ for (byte[] pos : possible_bytecodes) {
+ if (Arrays.equals(pos, real_codes)) {
+ return;
+ }
+ }
+ System.out.println("Unexpected bytecodes for " + m);
+ System.out.println("Received: " + Arrays.toString(real_codes));
+ System.out.println("Expected one of:");
+ for (byte[] pos : possible_bytecodes) {
+ System.out.println("\t" + Arrays.toString(pos));
+ }
+ }
+
+ public static void run() throws Exception {
+ Class<?> target = getClassLoader().loadClass("art.Target");
+ CheckMethodBytes(target.getDeclaredMethod("doNothing"), DO_NOTHING_BYTECODES);
+ CheckMethodBytes(target.getDeclaredMethod("staticNothing"), STATIC_NOTHING_BYTECODES);
+ CheckMethodBytes(target.getDeclaredMethod("sayHi"), SAY_HI_NOTHING_BYTECODES);
+ }
+
+ public static native byte[] getBytecodes(Method m);
+}
diff --git a/test/458-checker-instruct-simplification/build b/test/458-checker-instruct-simplification/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/458-checker-instruct-simplification/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/458-checker-instruct-simplification/smali/SmaliTests.smali b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
index 6845961f39..a8d7d94e46 100644
--- a/test/458-checker-instruct-simplification/smali/SmaliTests.smali
+++ b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
@@ -327,3 +327,144 @@
return v0
.end method
+
+# Test simplification of the `~~var` pattern.
+# The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`.
+
+## CHECK-START: long SmaliTests.NotNot1(long) instruction_simplifier (before)
+## CHECK-DAG: <<Arg:j\d+>> ParameterValue
+## CHECK-DAG: <<Not1:j\d+>> Not [<<Arg>>]
+## CHECK-DAG: <<Not2:j\d+>> Not [<<Not1>>]
+## CHECK-DAG: Return [<<Not2>>]
+
+## CHECK-START: long SmaliTests.NotNot1(long) instruction_simplifier (after)
+## CHECK-DAG: <<Arg:j\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+
+## CHECK-START: long SmaliTests.NotNot1(long) instruction_simplifier (after)
+## CHECK-NOT: Not
+
+.method public static NotNot1(J)J
+ .registers 4
+ .param p0, "arg" # J
+
+ .prologue
+ sget-boolean v0, LMain;->doThrow:Z
+
+ # if (doThrow) throw new Error();
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~~arg
+ not-long v0, p0
+ not-long v0, v0
+ return-wide v0
+.end method
+
+## CHECK-START: int SmaliTests.NotNot2(int) instruction_simplifier (before)
+## CHECK-DAG: <<Arg:i\d+>> ParameterValue
+## CHECK-DAG: <<Not1:i\d+>> Not [<<Arg>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<Not1>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.NotNot2(int) instruction_simplifier (after)
+## CHECK-DAG: <<Arg:i\d+>> ParameterValue
+## CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Not>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.NotNot2(int) instruction_simplifier (after)
+## CHECK: Not
+## CHECK-NOT: Not
+
+.method public static NotNot2(I)I
+ .registers 3
+ .param p0, "arg" # I
+
+ .prologue
+ sget-boolean v1, LMain;->doThrow:Z
+
+ # if (doThrow) throw new Error();
+ if-eqz v1, :cond_a
+ new-instance v1, Ljava/lang/Error;
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+ throw v1
+
+ :cond_a
+ # temp = ~arg; return temp + ~temp;
+ not-int v0, p0
+ not-int v1, v0
+ add-int/2addr v1, v0
+
+ return v1
+.end method
+
+# Test simplification of double Boolean negation. Note that sometimes
+# both negations can be removed but we only expect the simplifier to
+# remove the second.
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier (before)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
+## CHECK-DAG: <<NotResult:i\d+>> Xor [<<Result>>,<<Const1>>]
+## CHECK-DAG: Return [<<NotResult>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
+## CHECK-DAG: <<NotResult:z\d+>> BooleanNot [<<Result>>]
+## CHECK-DAG: Return [<<NotResult>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier$after_inlining (before)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+## CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
+## CHECK-DAG: Return [<<NotNotArg>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier$after_inlining (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+## CHECK-DAG: Return [<<Arg>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) dead_code_elimination$final (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+
+.method public static NegateValue(Z)Z
+ .registers 2
+ .param p0, "arg" # Z
+
+ .prologue
+
+ # return !arg
+ xor-int/lit8 v0, p0, 0x1
+ return v0
+.end method
+
+
+.method public static NotNotBool(Z)Z
+ .registers 2
+ .param p0, "arg" # Z
+
+ .prologue
+ sget-boolean v0, LMain;->doThrow:Z
+
+ # if (doThrow) throw new Error();
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !Negate(arg)
+ invoke-static {p0}, LSmaliTests;->NegateValue(Z)Z
+ move-result v0
+ xor-int/lit8 v0, v0, 0x1
+
+ return v0
+.end method
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 529ea5ba9c..5c36ce9982 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -982,17 +982,18 @@ public class Main {
*/
/// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (before)
- /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
- /// CHECK-DAG: <<Not1:j\d+>> Not [<<Arg>>]
- /// CHECK-DAG: <<Not2:j\d+>> Not [<<Not1>>]
- /// CHECK-DAG: Return [<<Not2>>]
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<ConstNeg1:j\d+>> LongConstant -1
+ /// CHECK-DAG: <<Not1:j\d+>> Xor [<<Arg>>,<<ConstNeg1>>]
+ /// CHECK-DAG: <<Not2:j\d+>> Xor [<<Not1>>,<<ConstNeg1>>]
+ /// CHECK-DAG: Return [<<Not2>>]
/// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
/// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after)
- /// CHECK-NOT: Not
+ /// CHECK-NOT: Xor
public static long $noinline$NotNot1(long arg) {
if (doThrow) { throw new Error(); }
@@ -1000,11 +1001,12 @@ public class Main {
}
/// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (before)
- /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
- /// CHECK-DAG: <<Not1:i\d+>> Not [<<Arg>>]
- /// CHECK-DAG: <<Not2:i\d+>> Not [<<Not1>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
- /// CHECK-DAG: Return [<<Add>>]
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ConstNeg1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<Arg>>,<<ConstNeg1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<Not1>>,<<ConstNeg1>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
+ /// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
@@ -1016,6 +1018,9 @@ public class Main {
/// CHECK: Not
/// CHECK-NOT: Not
+ /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after)
+ /// CHECK-NOT: Xor
+
public static int $noinline$NotNot2(int arg) {
if (doThrow) { throw new Error(); }
int temp = ~arg;
@@ -1180,30 +1185,46 @@ public class Main {
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
- /// CHECK-DAG: <<NotResult:i\d+>> Xor [<<Result>>,<<Const1>>]
- /// CHECK-DAG: Return [<<NotResult>>]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect method_name:Main.NegateValue
+ /// CHECK-DAG: <<NotResult:z\d+>> NotEqual [<<Result>>,<<Const1>>]
+ /// CHECK-DAG: If [<<NotResult>>]
+
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after)
+ /// CHECK-NOT: NotEqual
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
- /// CHECK-DAG: <<NotResult:z\d+>> BooleanNot [<<Result>>]
- /// CHECK-DAG: Return [<<NotResult>>]
+ /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect method_name:Main.NegateValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
- /// CHECK-DAG: Return [<<NotNotArg>>]
+ /// CHECK-NOT: BooleanNot [<<Arg>>]
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<Const1>>,<<Const0>>,<<Sel>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: Return [<<Arg>>]
+ /// CHECK: BooleanNot [<<Arg>>]
+ /// CHECK-NEXT: Goto
+
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: Select
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) dead_code_elimination$final (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-NOT: BooleanNot [<<Arg>>]
/// CHECK-DAG: Return [<<Arg>>]
public static boolean NegateValue(boolean arg) {
@@ -1882,7 +1903,18 @@ public class Main {
}
}
- public static int $noinline$runSmaliTestConst(String name, int arg) {
+ public static boolean $noinline$runSmaliTestBoolean(String name, boolean input) {
+ if (doThrow) { throw new Error(); }
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, boolean.class);
+ return (Boolean) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+ public static int $noinline$runSmaliTestInt(String name, int arg) {
if (doThrow) { throw new Error(); }
try {
Class<?> c = Class.forName("SmaliTests");
@@ -1893,6 +1925,17 @@ public class Main {
}
}
+ public static long $noinline$runSmaliTestLong(String name, long arg) {
+ if (doThrow) { throw new Error(); }
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, long.class);
+ return (Long) m.invoke(null, arg);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
/// CHECK-START: int Main.$noinline$intUnnecessaryShiftMasking(int, int) instruction_simplifier (before)
/// CHECK: <<Value:i\d+>> ParameterValue
/// CHECK: <<Shift:i\d+>> ParameterValue
@@ -2188,7 +2231,9 @@ public class Main {
assertIntEquals(1, $noinline$NegSub1(arg, arg + 1));
assertIntEquals(1, $noinline$NegSub2(arg, arg + 1));
assertLongEquals(arg, $noinline$NotNot1(arg));
+ assertLongEquals(arg, $noinline$runSmaliTestLong("NotNot1", arg));
assertIntEquals(-1, $noinline$NotNot2(arg));
+ assertIntEquals(-1, $noinline$runSmaliTestInt("NotNot2", arg));
assertIntEquals(-(arg + arg + 1), $noinline$SubNeg1(arg, arg + 1));
assertIntEquals(-(arg + arg + 1), $noinline$SubNeg2(arg, arg + 1));
assertLongEquals(-(2 * arg + 1), $noinline$SubNeg3(arg, arg + 1));
@@ -2197,7 +2242,9 @@ public class Main {
assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false));
assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false));
assertBooleanEquals(true, $noinline$NotNotBool(true));
+ assertBooleanEquals(true, $noinline$runSmaliTestBoolean("NotNotBool", true));
assertBooleanEquals(false, $noinline$NotNotBool(false));
+ assertBooleanEquals(false, $noinline$runSmaliTestBoolean("NotNotBool", false));
assertFloatEquals(50.0f, $noinline$Div2(100.0f));
assertDoubleEquals(75.0, $noinline$Div2(150.0));
assertFloatEquals(-400.0f, $noinline$DivMP25(100.0f));
@@ -2309,11 +2356,11 @@ public class Main {
}
}
- assertIntEquals(0, $noinline$runSmaliTestConst("AddSubConst", 1));
- assertIntEquals(3, $noinline$runSmaliTestConst("SubAddConst", 2));
- assertIntEquals(-16, $noinline$runSmaliTestConst("SubSubConst1", 3));
- assertIntEquals(-5, $noinline$runSmaliTestConst("SubSubConst2", 4));
- assertIntEquals(26, $noinline$runSmaliTestConst("SubSubConst3", 5));
+ assertIntEquals(0, $noinline$runSmaliTestInt("AddSubConst", 1));
+ assertIntEquals(3, $noinline$runSmaliTestInt("SubAddConst", 2));
+ assertIntEquals(-16, $noinline$runSmaliTestInt("SubSubConst1", 3));
+ assertIntEquals(-5, $noinline$runSmaliTestInt("SubSubConst2", 4));
+ assertIntEquals(26, $noinline$runSmaliTestInt("SubSubConst3", 5));
assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3));
assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3 + 32));
assertLongEquals(0xffffffffffffeaf3L, $noinline$longUnnecessaryShiftMasking(0xabcdef0123456789L, 50));
diff --git a/test/536-checker-intrinsic-optimization/build b/test/536-checker-intrinsic-optimization/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/536-checker-intrinsic-optimization/smali/SmaliTests.smali b/test/536-checker-intrinsic-optimization/smali/SmaliTests.smali
new file mode 100644
index 0000000000..6612faeb77
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/smali/SmaliTests.smali
@@ -0,0 +1,64 @@
+# Copyright (C) 2017 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 LSmaliTests;
+.super Ljava/lang/Object;
+
+## CHECK-START: char SmaliTests.stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
+## CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
+## CHECK-DAG: Return [<<Char>>]
+
+## CHECK-START: char SmaliTests.stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+## CHECK-DAG: <<String:l\d+>> ParameterValue
+## CHECK-DAG: <<Pos:i\d+>> ParameterValue
+## CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+## CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+## CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+## CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
+## CHECK-DAG: Return [<<Char>>]
+
+## CHECK-START: char SmaliTests.stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+## CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+.method public static stringCharAtCatch(Ljava/lang/String;I)C
+ .registers 4
+ .param p0, "s" # Ljava/lang/String;
+ .param p1, "pos" # I
+
+ .prologue
+
+ # if (doThrow) { throw new Error(); }
+ sget-boolean v1, LMain;->doThrow:Z
+ if-eqz v1, :doThrow_false
+ new-instance v1, Ljava/lang/Error;
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+ throw v1
+
+ :doThrow_false
+ :try_start
+ # tmp = s.charAt(pos)
+ invoke-virtual {p0, p1}, Ljava/lang/String;->charAt(I)C
+ :try_end
+ .catch Ljava/lang/StringIndexOutOfBoundsException; {:try_start .. :try_end} :catch
+
+ # return tmp
+ move-result v1
+ return v1
+
+ :catch
+ # return '\0'
+ move-exception v0
+ const/4 v1, 0x0
+ return v1
+.end method
+
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index e395e283e0..3dce23fb31 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
public class Main {
public static boolean doThrow = false;
@@ -83,7 +84,9 @@ public class Main {
}
assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7));
+ assertCharEquals('7', $noinline$runSmaliTest("stringCharAtCatch", "0123456789", 7));
assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10));
+ assertCharEquals('\0', $noinline$runSmaliTest("stringCharAtCatch","0123456789", 10));
assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc"));
assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3));
@@ -166,17 +169,21 @@ public class Main {
}
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0
/// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
- /// CHECK-DAG: Return [<<Char>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Char>>,<<Int>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
/// CHECK-DAG: <<String:l\d+>> ParameterValue
/// CHECK-DAG: <<Pos:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0
/// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
/// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
/// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
- /// CHECK-DAG: Return [<<Char>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Char>>,<<Int>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
/// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
@@ -421,4 +428,14 @@ public class Main {
static String myString;
static Object myObject;
+
+ public static char $noinline$runSmaliTest(String name, String str, int pos) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, String.class, int.class);
+ return (Character) m.invoke(null, str, pos);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
}
diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/565-checker-doublenegbitwise/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
new file mode 100644
index 0000000000..2e0802276e
--- /dev/null
+++ b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
@@ -0,0 +1,405 @@
+# Copyright (C) 2017 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 LSmaliTests;
+.super Ljava/lang/Object;
+
+#
+# Test transformation of Not/Not/And into Or/Not.
+#
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<And>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
+## CHECK-DAG: <<Not:i\d+>> Not [<<Or>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+## CHECK-NOT: And
+.method public static $opt$noinline$andToOr(II)I
+ .registers 4
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~a & ~b;
+ not-int v0, p0
+ not-int v1, p1
+ and-int/2addr v0, v1
+
+ return v0
+.end method
+
+# Test transformation of Not/Not/And into Or/Not for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
+## CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
+## CHECK-DAG: <<And:i\d+>> And [<<NotP1>>,<<NotP2>>]
+## CHECK-DAG: Return [<<And>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+## CHECK-DAG: Return [<<BooleanNot>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: BooleanNot
+## CHECK-NOT: BooleanNot
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: And
+.method public static $opt$noinline$booleanAndToOr(ZZ)Z
+ .registers 4
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !a & !b;
+ xor-int/lit8 v0, p0, 0x1
+ xor-int/lit8 v1, p1, 0x1
+ and-int/2addr v0, v1
+ return v0
+.end method
+
+# Test transformation of Not/Not/Or into And/Not.
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
+## CHECK-DAG: <<P1:j\d+>> ParameterValue
+## CHECK-DAG: <<P2:j\d+>> ParameterValue
+## CHECK-DAG: <<Not1:j\d+>> Not [<<P1>>]
+## CHECK-DAG: <<Not2:j\d+>> Not [<<P2>>]
+## CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+## CHECK-DAG: <<P1:j\d+>> ParameterValue
+## CHECK-DAG: <<P2:j\d+>> ParameterValue
+## CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>]
+## CHECK-DAG: <<Not:j\d+>> Not [<<And>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$orToAnd(JJ)J
+ .registers 8
+ .param p0, "a" # J
+ .param p2, "b" # J
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~a | ~b;
+ not-long v0, p0
+ not-long v2, p2
+ or-long/2addr v0, v2
+ return-wide v0
+.end method
+
+# Test transformation of Not/Not/Or into Or/And for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
+## CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
+## CHECK-DAG: <<Or:i\d+>> Or [<<NotP1>>,<<NotP2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+## CHECK-DAG: Return [<<BooleanNot>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: BooleanNot
+## CHECK-NOT: BooleanNot
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$booleanOrToAnd(ZZ)Z
+ .registers 4
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !a | !b;
+ xor-int/lit8 v0, p0, 0x1
+ xor-int/lit8 v1, p1, 0x1
+ or-int/2addr v0, v1
+ return v0
+.end method
+
+# Test that the transformation copes with inputs being separated from the
+# bitwise operations.
+# This is a regression test. The initial logic was inserting the new bitwise
+# operation incorrectly.
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<AddP1>>]
+## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<AddP2>>]
+## CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+## CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
+## CHECK-DAG: <<Not:i\d+>> Not [<<And>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$regressInputsAway(II)I
+ .registers 7
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v4, LSmaliTests;->doThrow:Z
+ if-eqz v4, :cond_a
+ new-instance v4, Ljava/lang/Error;
+ invoke-direct {v4}, Ljava/lang/Error;-><init>()V
+ throw v4
+
+ :cond_a
+ # int a1 = a + 1;
+ add-int/lit8 v0, p0, 0x1
+ # int not_a1 = ~a1;
+ not-int v2, v0
+ # int b1 = b + 1;
+ add-int/lit8 v1, p1, 0x1
+ # int not_b1 = ~b1;
+ not-int v3, v1
+
+ # return not_a1 | not_b1
+ or-int v4, v2, v3
+ return v4
+.end method
+
+# Test transformation of Not/Not/Xor into Xor.
+
+# See first note above.
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
+## CHECK-NOT: Not
+.method public static $opt$noinline$notXorToXor(II)I
+ .registers 4
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~a ^ ~b;
+ not-int v0, p0
+ not-int v1, p1
+ xor-int/2addr v0, v1
+ return v0
+.end method
+
+# Test transformation of Not/Not/Xor into Xor for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
+## CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<NotP1>>,<<NotP2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: BooleanNot
+.method public static $opt$noinline$booleanNotXorToXor(ZZ)Z
+ .registers 4
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !a ^ !b;
+ xor-int/lit8 v0, p0, 0x1
+ xor-int/lit8 v1, p1, 0x1
+ xor-int/2addr v0, v1
+ return v0
+.end method
+
+# Check that no transformation is done when one Not has multiple uses.
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$notMultipleUses(II)I
+ .registers 5
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v1, LSmaliTests;->doThrow:Z
+ if-eqz v1, :cond_a
+ new-instance v1, Ljava/lang/Error;
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+ throw v1
+
+ :cond_a
+ # int tmp = ~b;
+ not-int v0, p1
+ # return (tmp & 0x1) + (~a & tmp);
+ and-int/lit8 v1, v0, 0x1
+ not-int v2, p0
+ and-int/2addr v2, v0
+ add-int/2addr v1, v2
+ return v1
+.end method
+
+# static fields
+.field static doThrow:Z # boolean
+
+# direct methods
+.method static constructor <clinit>()V
+ .registers 1
+
+ .prologue
+ # doThrow = false
+ const/4 v0, 0x0
+ sput-boolean v0, LSmaliTests;->doThrow:Z
+ return-void
+.end method
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index 5ccc648076..816cafc2ba 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
// A dummy value to defeat inlining of these routines.
@@ -31,31 +33,53 @@ public class Main {
}
}
+ public static void assertEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static <T> T $noinline$runSmaliTest(String name, Class<T> klass, T input1, T input2) {
+ if (doThrow) { throw new Error(); }
+
+ Class<T> inputKlass = (Class<T>)input1.getClass();
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, klass, klass);
+ return inputKlass.cast(m.invoke(null, input1, input2));
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
/**
* Test transformation of Not/Not/And into Or/Not.
*/
+ // Note: before the instruction_simplifier pass, Xor's are used instead of
+ // Not's (the simplification happens during the same pass).
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<And>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+ /// CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<And>>]
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
- /// CHECK: <<Not:i\d+>> Not [<<Or>>]
- /// CHECK: Return [<<Not>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
+ /// CHECK-DAG: <<Not:i\d+>> Not [<<Or>>]
+ /// CHECK-DAG: Return [<<Not>>]
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK: Not
- /// CHECK-NOT: Not
+ /// CHECK-DAG: Not
+ /// CHECK-NOT: Not
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK-NOT: And
+ /// CHECK-NOT: And
public static int $opt$noinline$andToOr(int a, int b) {
if (doThrow) throw new Error();
@@ -70,28 +94,29 @@ public class Main {
* same pass.
*/
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (before)
- /// CHECK: <<P1:z\d+>> ParameterValue
- /// CHECK: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
- /// CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
- /// CHECK: <<And:i\d+>> And [<<NotP1>>,<<NotP2>>]
- /// CHECK: Return [<<And>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (after)
- /// CHECK: <<Cond1:z\d+>> ParameterValue
- /// CHECK: <<Cond2:z\d+>> ParameterValue
- /// CHECK: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>]
- /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
- /// CHECK: Return [<<BooleanNot>>]
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<P1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK-DAG: <<And:i\d+>> And [<<Select2>>,<<Select1>>]
+ /// CHECK-DAG: Return [<<And>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<Cond2>>,<<Cond1>>]
+ /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+ /// CHECK-DAG: Return [<<BooleanNot>>]
/// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK: BooleanNot
- /// CHECK-NOT: BooleanNot
+ /// CHECK-DAG: BooleanNot
+ /// CHECK-NOT: BooleanNot
/// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: And
+ /// CHECK-NOT: And
public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
if (doThrow) throw new Error();
@@ -102,27 +127,30 @@ public class Main {
* Test transformation of Not/Not/Or into And/Not.
*/
+ // See note above.
+ // The second Xor has its arguments reversed for no obvious reason.
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
- /// CHECK: <<P1:j\d+>> ParameterValue
- /// CHECK: <<P2:j\d+>> ParameterValue
- /// CHECK: <<Not1:j\d+>> Not [<<P1>>]
- /// CHECK: <<Not2:j\d+>> Not [<<P2>>]
- /// CHECK: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<Or>>]
+ /// CHECK-DAG: <<P1:j\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:j\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:j\d+>> LongConstant -1
+ /// CHECK-DAG: <<Not1:j\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<Not2:j\d+>> Xor [<<CstM1>>,<<P2>>]
+ /// CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<Or>>]
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK: <<P1:j\d+>> ParameterValue
- /// CHECK: <<P2:j\d+>> ParameterValue
- /// CHECK: <<And:j\d+>> And [<<P1>>,<<P2>>]
- /// CHECK: <<Not:j\d+>> Not [<<And>>]
- /// CHECK: Return [<<Not>>]
+ /// CHECK-DAG: <<P1:j\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:j\d+>> ParameterValue
+ /// CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>]
+ /// CHECK-DAG: <<Not:j\d+>> Not [<<And>>]
+ /// CHECK-DAG: Return [<<Not>>]
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK: Not
- /// CHECK-NOT: Not
+ /// CHECK-DAG: Not
+ /// CHECK-NOT: Not
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static long $opt$noinline$orToAnd(long a, long b) {
if (doThrow) throw new Error();
@@ -137,28 +165,29 @@ public class Main {
* same pass.
*/
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (before)
- /// CHECK: <<P1:z\d+>> ParameterValue
- /// CHECK: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
- /// CHECK: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
- /// CHECK: <<Or:i\d+>> Or [<<NotP1>>,<<NotP2>>]
- /// CHECK: Return [<<Or>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (after)
- /// CHECK: <<Cond1:z\d+>> ParameterValue
- /// CHECK: <<Cond2:z\d+>> ParameterValue
- /// CHECK: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>]
- /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
- /// CHECK: Return [<<BooleanNot>>]
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<P1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<Select2>>,<<Select1>>]
+ /// CHECK-DAG: Return [<<Or>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<And:i\d+>> And [<<Cond2>>,<<Cond1>>]
+ /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+ /// CHECK-DAG: Return [<<BooleanNot>>]
/// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK: BooleanNot
- /// CHECK-NOT: BooleanNot
+ /// CHECK-DAG: BooleanNot
+ /// CHECK-NOT: BooleanNot
/// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
if (doThrow) throw new Error();
@@ -173,32 +202,33 @@ public class Main {
*/
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Cst1:i\d+>> IntConstant 1
- /// CHECK: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
- /// CHECK: <<Not1:i\d+>> Not [<<AddP1>>]
- /// CHECK: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
- /// CHECK: <<Not2:i\d+>> Not [<<AddP2>>]
- /// CHECK: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<Or>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<AddP1>>,<<CstM1>>]
+ /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<AddP2>>,<<CstM1>>]
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<Or>>]
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Cst1:i\d+>> IntConstant 1
- /// CHECK: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
- /// CHECK: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
- /// CHECK: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
- /// CHECK: <<Not:i\d+>> Not [<<And>>]
- /// CHECK: Return [<<Not>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+ /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+ /// CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
+ /// CHECK-DAG: <<Not:i\d+>> Not [<<And>>]
+ /// CHECK-DAG: Return [<<Not>>]
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK: Not
- /// CHECK-NOT: Not
+ /// CHECK-DAG: Not
+ /// CHECK-NOT: Not
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static int $opt$noinline$regressInputsAway(int a, int b) {
if (doThrow) throw new Error();
@@ -215,21 +245,22 @@ public class Main {
// See first note above.
/// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<Xor>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
- /// CHECK: Return [<<Xor>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
+ /// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Not
+ /// CHECK-NOT: Not
public static int $opt$noinline$notXorToXor(int a, int b) {
if (doThrow) throw new Error();
@@ -244,23 +275,24 @@ public class Main {
* same pass.
*/
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (before)
- /// CHECK: <<P1:z\d+>> ParameterValue
- /// CHECK: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
- /// CHECK: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
- /// CHECK: <<Xor:i\d+>> Xor [<<NotP1>>,<<NotP2>>]
- /// CHECK: Return [<<Xor>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (after)
- /// CHECK: <<Cond1:z\d+>> ParameterValue
- /// CHECK: <<Cond2:z\d+>> ParameterValue
- /// CHECK: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>]
- /// CHECK: Return [<<Xor>>]
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<P1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Select2>>,<<Select1>>]
+ /// CHECK-DAG: Return [<<Xor>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond2>>,<<Cond1>>]
+ /// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: BooleanNot
+ /// CHECK-NOT: BooleanNot
public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
if (doThrow) throw new Error();
@@ -272,29 +304,30 @@ public class Main {
*/
/// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<One:i\d+>> IntConstant 1
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<And2:i\d+>> And [<<Not2>>,<<One>>]
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
- /// CHECK: Return [<<Add>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+ /// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<One:i\d+>> IntConstant 1
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<And2:i\d+>> And [<<Not2>>,<<One>>]
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
- /// CHECK: Return [<<Add>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+ /// CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+ /// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static int $opt$noinline$notMultipleUses(int a, int b) {
if (doThrow) throw new Error();
@@ -304,8 +337,20 @@ public class Main {
public static void main(String[] args) {
assertIntEquals(~0xff, $opt$noinline$andToOr(0xf, 0xff));
+ assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOr", int.class, 0xf, 0xff));
+ assertEquals(true, $opt$noinline$booleanAndToOr(false, false));
+ assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOr", boolean.class, false, false));
assertLongEquals(~0xf, $opt$noinline$orToAnd(0xf, 0xff));
+ assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAnd", long.class, 0xfL, 0xffL));
+ assertEquals(false, $opt$noinline$booleanOrToAnd(true, true));
+ assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAnd", boolean.class, true, true));
+ assertIntEquals(-1, $opt$noinline$regressInputsAway(0xf, 0xff));
+ assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAway", int.class, 0xf, 0xff));
assertIntEquals(0xf0, $opt$noinline$notXorToXor(0xf, 0xff));
+ assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXor", int.class, 0xf, 0xff));
+ assertEquals(true, $opt$noinline$booleanNotXorToXor(true, false));
+ assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXor", boolean.class, true, false));
assertIntEquals(~0xff, $opt$noinline$notMultipleUses(0xf, 0xff));
+ assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUses", int.class, 0xf, 0xff));
}
}
diff --git a/test/586-checker-null-array-get/build b/test/586-checker-null-array-get/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/586-checker-null-array-get/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/586-checker-null-array-get/smali/SmaliTests.smali b/test/586-checker-null-array-get/smali/SmaliTests.smali
new file mode 100644
index 0000000000..f58af360fc
--- /dev/null
+++ b/test/586-checker-null-array-get/smali/SmaliTests.smali
@@ -0,0 +1,96 @@
+# Copyright (C) 2017 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 LSmaliTests;
+.super Ljava/lang/Object;
+
+## CHECK-START: void SmaliTests.bar() load_store_elimination (after)
+## CHECK-DAG: <<Null:l\d+>> NullConstant
+## CHECK-DAG: <<BoundType:l\d+>> BoundType [<<Null>>]
+## CHECK-DAG: <<CheckL:l\d+>> NullCheck [<<BoundType>>]
+## CHECK-DAG: <<GetL0:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<GetL1:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<GetL2:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<GetL3:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<CheckJ:l\d+>> NullCheck [<<Null>>]
+## CHECK-DAG: <<GetJ0:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+## CHECK-DAG: <<GetJ1:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+## CHECK-DAG: <<GetJ2:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+## CHECK-DAG: <<GetJ3:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+.method public static bar()V
+ .registers 7
+
+ .prologue
+ const/4 v6, 0x3
+ const/4 v5, 0x2
+ const/4 v4, 0x1
+ const/4 v3, 0x0
+
+ # We create multiple accesses that will lead the bounds check
+ # elimination pass to add a HDeoptimize. Not having the bounds check helped
+ # the load store elimination think it could merge two ArrayGet with different
+ # types.
+
+ # String[] array = (String[])getNull();
+ invoke-static {}, LMain;->getNull()Ljava/lang/Object;
+ move-result-object v0
+ check-cast v0, [Ljava/lang/String;
+
+ # objectField = array[0];
+ aget-object v2, v0, v3
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+ # objectField = array[1];
+ aget-object v2, v0, v4
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+ # objectField = array[2];
+ aget-object v2, v0, v5
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+ # objectField = array[3];
+ aget-object v2, v0, v6
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+
+ # long[] longArray = getLongArray();
+ invoke-static {}, LMain;->getLongArray()[J
+ move-result-object v1
+
+ # longField = longArray[0];
+ aget-wide v2, v1, v3
+ sput-wide v2, LMain;->longField:J
+ # longField = longArray[1];
+ aget-wide v2, v1, v4
+ sput-wide v2, LMain;->longField:J
+ # longField = longArray[2];
+ aget-wide v2, v1, v5
+ sput-wide v2, LMain;->longField:J
+ # longField = longArray[3];
+ aget-wide v2, v1, v6
+ sput-wide v2, LMain;->longField:J
+
+ return-void
+.end method
+
+
+# static fields
+.field static doThrow:Z # boolean
+
+# direct methods
+.method static constructor <clinit>()V
+ .registers 1
+
+ .prologue
+ # doThrow = false
+ const/4 v0, 0x0
+ sput-boolean v0, LSmaliTests;->doThrow:Z
+ return-void
+.end method
diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java
index 0ea7d34043..09ebff16c2 100644
--- a/test/586-checker-null-array-get/src/Main.java
+++ b/test/586-checker-null-array-get/src/Main.java
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
class Test1 {
int[] iarr;
}
@@ -29,6 +32,18 @@ public class Main {
public static Test1 getNullTest1() { return null; }
public static Test2 getNullTest2() { return null; }
+ public static void $noinline$runSmaliTest(String name) throws Throwable {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ m.invoke(null);
+ } catch (InvocationTargetException ex) {
+ throw ex.getCause(); // re-raise expected exception.
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
public static void main(String[] args) {
try {
foo();
@@ -43,6 +58,15 @@ public class Main {
// Expected.
}
try {
+ $noinline$runSmaliTest("bar");
+ throw new Error("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ } catch (Throwable t) {
+ throw new Error("Unexpected Throwable", t);
+ }
+
+ try {
test1();
throw new Error("Expected NullPointerException");
} catch (NullPointerException e) {
@@ -62,7 +86,8 @@ public class Main {
/// CHECK-START: void Main.bar() load_store_elimination (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<Null>>]
+ /// CHECK-DAG: <<BoundFirst:l\d+>> BoundType [<<Null>>]
+ /// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<BoundFirst>>]
/// CHECK-DAG: <<CheckL:l\d+>> NullCheck [<<BoundType>>]
/// CHECK-DAG: <<GetL0:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
/// CHECK-DAG: <<GetL1:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
diff --git a/test/593-checker-boolean-2-integral-conv/build b/test/593-checker-boolean-2-integral-conv/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/593-checker-boolean-2-integral-conv/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
new file mode 100644
index 0000000000..00ebaaf451
--- /dev/null
+++ b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
@@ -0,0 +1,119 @@
+# Copyright (C) 2017 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 LSmaliTests;
+.super Ljava/lang/Object;
+
+# static fields
+.field public static booleanField:Z
+
+.method static constructor <clinit>()V
+ .registers 1
+
+ .prologue
+ const/4 v0, 0x1
+
+ # booleanField = true
+ sput-boolean v0, LSmaliTests;->booleanField:Z
+
+ return-void
+.end method
+
+## CHECK-START: long SmaliTests.booleanToLong(boolean) builder (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+## CHECK-DAG: If [<<Cond>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: Return [<<IToJ>>]
+
+## CHECK-START: long SmaliTests.booleanToLong(boolean) select_generator (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: Return [<<IToJ>>]
+
+## CHECK-START: long SmaliTests.booleanToLong(boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
+## CHECK-DAG: Return [<<ZToJ>>]
+.method public static booleanToLong(Z)J
+ .registers 3
+ .param p0, "b" # Z
+ .prologue
+
+ # return b ? 1 : 0;
+ if-eqz p0, :b_is_zero
+
+# :b_is_one
+ const/4 v0, 0x1
+
+ :l_return
+ int-to-long v0, v0
+ return-wide v0
+
+ :b_is_zero
+ const/4 v0, 0x0
+ goto :l_return
+.end method
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() builder (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: <<ZToJ:j\d+>> InvokeStaticOrDirect [<<Sget>>,<<Method>>]
+## CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<ZToJ>>]
+## CHECK-DAG: Return [<<JToI>>]
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() inliner (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: If [<<Sget>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+## CHECK-DAG: Return [<<JToI>>]
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() select_generator (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+## CHECK-DAG: Return [<<JToI>>]
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() instruction_simplifier$after_bce (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: Return [<<Sget>>]
+.method public static longToIntOfBoolean()I
+ .registers 3
+ .prologue
+
+ # long l = booleanToLong(booleanField);
+ sget-boolean v2, LSmaliTests;->booleanField:Z
+ invoke-static {v2}, LSmaliTests;->booleanToLong(Z)J
+ move-result-wide v0
+
+ # return (int) l;
+ long-to-int v2, v0
+ return v2
+.end method
diff --git a/test/593-checker-boolean-2-integral-conv/src/Main.java b/test/593-checker-boolean-2-integral-conv/src/Main.java
index b4c91c8db6..3503b2e877 100644
--- a/test/593-checker-boolean-2-integral-conv/src/Main.java
+++ b/test/593-checker-boolean-2-integral-conv/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
public static void main(String args[]) {
@@ -22,8 +24,10 @@ public class Main {
expectEqualsChar((char)1, booleanToChar(true));
expectEqualsInt(1, booleanToInt(true));
expectEqualsLong(1L, booleanToLong(true));
+ expectEqualsLong(1L, $noinline$runSmaliTest("booleanToLong", true));
expectEqualsInt(1, longToIntOfBoolean());
+ expectEqualsInt(1, $noinline$runSmaliTest("longToIntOfBoolean"));
System.out.println("passed");
}
@@ -132,26 +136,34 @@ public class Main {
/// CHECK-START: long Main.booleanToLong(boolean) builder (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: <<IZero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<IZero>>]
/// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: Return [<<IToJ>>]
+ /// CHECK-DAG: <<Phi:j\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: Return [<<IToJ>>]
+ /// CHECK-NOT: IntConstant
+ /// CHECK-NOT: Equal
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Phi
- /// CHECK-START: long Main.booleanToLong(boolean) instruction_simplifier$after_bce (after)
+ /// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
- /// CHECK-DAG: Return [<<ZToJ>>]
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
+ /// CHECK-DAG: <<Sel:j\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Sel>>]
+
+ // As of now, the code is not optimized any further than the above.
+ // TODO: Re-enable checks below after simplifier is updated to handle this pattern: b/63064517
+
+ // CHECK-START: long Main.booleanToLong(boolean) instruction_simplifier$after_bce (after)
+ // CHECK: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
+ // CHECK-DAG: Return [<<ZToJ>>]
static long booleanToLong(boolean b) {
return b ? 1 : 0;
@@ -166,29 +178,36 @@ public class Main {
/// CHECK-START: int Main.longToIntOfBoolean() inliner (after)
/// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
/// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
/// CHECK-DAG: If [<<Sget>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+ /// CHECK-DAG: <<Phi:j\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<Phi>>]
/// CHECK-DAG: Return [<<JToI>>]
+ /// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
+ /// CHECK-NOT: IntConstant
+ /// CHECK-NOT: Equal
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Phi
+
/// CHECK-START: int Main.longToIntOfBoolean() select_generator (after)
/// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
/// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+ /// CHECK-DAG: <<Sel:j\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<Sel>>]
/// CHECK-DAG: Return [<<JToI>>]
- /// CHECK-START: int Main.longToIntOfBoolean() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
- /// CHECK-DAG: Return [<<Sget>>]
+ // As of now, the code is not optimized any further than the above.
+ // TODO: Re-enable checks below after simplifier is updated to handle this pattern: b/63064517
+
+ // CHECK-START: int Main.longToIntOfBoolean() instruction_simplifier$after_bce (after)
+ // CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ // CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+ // CHECK-DAG: Return [<<Sget>>]
static int longToIntOfBoolean() {
long l = booleanToLong(booleanField);
@@ -226,6 +245,26 @@ public class Main {
}
}
+ public static long $noinline$runSmaliTest(String name, boolean input) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, boolean.class);
+ return (Long) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+ public static int $noinline$runSmaliTest(String name) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ return (Integer) m.invoke(null);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
public static boolean booleanField = true;
diff --git a/test/593-checker-shift-and-simplifier/expected.txt b/test/593-checker-shift-and-simplifier/expected.txt
index b0aad4deb5..f8d85db6ac 100644
--- a/test/593-checker-shift-and-simplifier/expected.txt
+++ b/test/593-checker-shift-and-simplifier/expected.txt
@@ -1 +1,2 @@
passed
+passed
diff --git a/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali b/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali
new file mode 100644
index 0000000000..6b0d68306b
--- /dev/null
+++ b/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali
@@ -0,0 +1,58 @@
+# Copyright (C) 2017 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 LSmaliTests;
+.super Ljava/lang/Object;
+
+# A very particular set of operations that caused a double removal by the
+# ARM64 simplifier doing "forward" removals (b/27851582).
+
+## CHECK-START-ARM: int SmaliTests.operations() instruction_simplifier_arm (before)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+## CHECK-DAG: And [<<Not>>,<<Shl>>]
+
+## CHECK-START-ARM: int SmaliTests.operations() instruction_simplifier_arm (after)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+
+## CHECK-START-ARM64: int SmaliTests.operations() instruction_simplifier_arm64 (before)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+## CHECK-DAG: And [<<Not>>,<<Shl>>]
+
+## CHECK-START-ARM64: int SmaliTests.operations() instruction_simplifier_arm64 (after)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+.method public static operations()I
+ .registers 6
+ .prologue
+
+ # int r = a[0];
+ sget-object v4, LMain;->a:[I
+ const/4 v5, 0x0
+ aget v2, v4, v5
+ # int n = ~r;
+ not-int v1, v2
+ # int s = r << 2;
+ shl-int/lit8 v3, v2, 0x2
+ # int a = s & n;
+ and-int v0, v3, v1
+ # return a
+ return v0
+.end method
diff --git a/test/593-checker-shift-and-simplifier/src/Main.java b/test/593-checker-shift-and-simplifier/src/Main.java
index c9826bc546..f0ef0e6949 100644
--- a/test/593-checker-shift-and-simplifier/src/Main.java
+++ b/test/593-checker-shift-and-simplifier/src/Main.java
@@ -14,30 +14,20 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
- private static int[] a = { 10 };
+ static int[] a = { 10 };
// A very particular set of operations that caused a double removal by the
// ARM64 simplifier doing "forward" removals (b/27851582).
- /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (before)
- /// CHECK-DAG: <<Get:i\d+>> ArrayGet
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
- /// CHECK-DAG: And [<<Not>>,<<Shl>>]
- //
/// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
/// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
- /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (before)
- /// CHECK-DAG: <<Get:i\d+>> ArrayGet
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
- /// CHECK-DAG: And [<<Not>>,<<Shl>>]
- //
/// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
@@ -56,5 +46,21 @@ public class Main {
} else {
System.out.println("passed");
}
+
+ if ($noinline$runSmaliTest("operations") != 32) {
+ System.out.println("failed");
+ } else {
+ System.out.println("passed");
+ }
+ }
+
+ public static int $noinline$runSmaliTest(String name) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ return (Integer) m.invoke(null);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
}
}
diff --git a/test/596-app-images/src/Main.java b/test/596-app-images/src/Main.java
index 8ee3c888b0..88d95f4162 100644
--- a/test/596-app-images/src/Main.java
+++ b/test/596-app-images/src/Main.java
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+import java.lang.reflect.Field;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
class Main {
static class Inner {
final public static int abc = 10;
@@ -46,13 +50,76 @@ class Main {
if (!checkInitialized(StaticFieldsInit.class))
System.out.println("StaticFieldsInit class is not initialized!");
- if (checkInitialized(StaticInternString.class))
- System.out.println("StaticInternString class is initialized!");
+ if (!checkInitialized(StaticInternString.class))
+ System.out.println("StaticInternString class is not initialized!");
+
+ StringBuffer sb = new StringBuffer();
+ sb.append("java.");
+ sb.append("abc.");
+ sb.append("Action");
+
+ String tmp = sb.toString();
+ String intern = tmp.intern();
+
+ assertNotEqual(tmp, intern, "Dynamically constructed String, not interned.");
+ assertEqual(intern, StaticInternString.intent, "Static encoded literal String not interned.");
+ assertEqual(BootInternedString.boot, BootInternedString.boot.intern(),
+ "Static encoded literal String not moved back to runtime intern table.");
+
+ try {
+ Field f = StaticInternString.class.getDeclaredField("intent");
+ assertEqual(intern, f.get(null), "String Literals are not interned properly.");
+
+ } catch (Exception e) {
+ System.out.println("Exception");
+ }
+
+ assertEqual(StaticInternString.getIntent(), StaticInternString2.getIntent(),
+ "String Literals are not intenred properly, App image static strings duplicated.");
+
+ // reload the class StaticInternString, check whether static strings interned properly
+ final String DEX_FILE = System.getenv("DEX_LOCATION") + "/596-app-images.jar";
+ final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path");
+
+ try {
+ Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new AssertionError("Counldn't find path class loader class");
+ }
+ Constructor<?> ctor =
+ pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class);
+ ClassLoader loader = (ClassLoader) ctor.newInstance(
+ DEX_FILE, LIBRARY_SEARCH_PATH, null);
+
+ Class<?> staticInternString = loader.loadClass("StaticInternString");
+
+ if (!checkAppImageContains(staticInternString)) {
+ System.out.println("Not loaded again.");
+ }
+ Method getIntent = staticInternString.getDeclaredMethod("getIntent");
+
+ assertEqual(StaticInternString.getIntent(), getIntent.invoke(staticInternString),
+ "Dynamically loaded app image's literal strings not interned properly.");
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
}
public static native boolean checkAppImageLoaded();
public static native boolean checkAppImageContains(Class<?> klass);
public static native boolean checkInitialized(Class<?> klass);
+
+ public static void assertEqual(Object a, Object b, String msg) {
+ if (a != b)
+ System.out.println(msg);
+ }
+
+ public static void assertNotEqual(Object a, Object b, String msg) {
+ if (a == b)
+ System.out.println(msg);
+ }
+
}
class StaticFields{
@@ -68,6 +135,21 @@ class StaticFieldsInit{
}
class StaticInternString {
- final public static String intern = "java.abc.Action";
+ final public static String intent = "java.abc.Action";
+ static public String getIntent() {
+ return intent;
+ }
+}
+
+class BootInternedString {
+ final public static String boot = "double";
+}
+
+class StaticInternString2 {
+ final public static String intent = "java.abc.Action";
+
+ static String getIntent() {
+ return intent;
+ }
}
diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java
index af205b074f..aca997e982 100644
--- a/test/623-checker-loop-regressions/src/Main.java
+++ b/test/623-checker-loop-regressions/src/Main.java
@@ -285,6 +285,9 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.string2Bytes(char[], java.lang.String) loop_optimization (after)
+ /// CHECK-NOT: VecLoad
+ //
/// CHECK-START-ARM64: void Main.string2Bytes(char[], java.lang.String) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -329,6 +332,13 @@ public class Main {
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<One>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.oneBoth(short[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.oneBoth(short[], char[]) loop_optimization (after)
/// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
@@ -369,6 +379,19 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Add>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.typeConv(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Load:d\d+>> VecLoad [{{l\d+}},<<Phi1>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Vadd:d\d+>> VecAdd [<<Load>>,<<Repl>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi1>>,<<Vadd>>] loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:b\d+>> ArrayGet [{{l\d+}},<<Phi2>>] loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Get>>,<<One>>] loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Add>>] loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi2>>,<<Cnv>>] loop:<<Loop2>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.typeConv(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<One:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<One>>] loop:none
diff --git a/test/633-checker-rtp-getclass/build b/test/633-checker-rtp-getclass/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/633-checker-rtp-getclass/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/633-checker-rtp-getclass/smali/SmaliTests.smali b/test/633-checker-rtp-getclass/smali/SmaliTests.smali
new file mode 100644
index 0000000000..9ea04498d7
--- /dev/null
+++ b/test/633-checker-rtp-getclass/smali/SmaliTests.smali
@@ -0,0 +1,65 @@
+# Copyright (C) 2017 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 LSmaliTests;
+.super Ljava/lang/Object;
+
+# Checker test to make sure the only inlined instruction is SubMain.bar.
+
+## CHECK-START: int SmaliTests.$opt$noinline$foo(Main) inliner (after)
+## CHECK-DAG: InvokeVirtual method_name:Main.foo
+## CHECK-DAG: <<Const:i\d+>> IntConstant 3
+## CHECK: begin_block
+## CHECK: BoundType klass:SubMain
+## CHECK: Return [<<Const>>]
+## CHECK-NOT: begin_block
+## CHECK: end_block
+.method public static $opt$noinline$foo(LMain;)I
+ .registers 3
+ .param p0, "o" # LMain;
+ .prologue
+
+ # if (doThrow) { throw new Error(); }
+ sget-boolean v0, LMain;->doThrow:Z
+ if-eqz v0, :doThrow_false
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :doThrow_false
+ # if (o.getClass() == Main.class || o.getClass() != SubMain.class)
+ invoke-virtual {p0}, LMain;->getClass()Ljava/lang/Class;
+ move-result-object v0
+ const-class v1, LMain;
+ if-eq v0, v1, :class_is_Main_and_not_SubMain
+
+ invoke-virtual {p0}, LMain;->getClass()Ljava/lang/Class;
+ move-result-object v0
+ const-class v1, LSubMain;
+ if-eq v0, v1, :else
+
+ :class_is_Main_and_not_SubMain
+ # return o.foo()
+ invoke-virtual {p0}, LMain;->foo()I
+ move-result v0
+ return v0
+
+ :else
+ # return o.bar()
+ invoke-virtual {p0}, LMain;->bar()I
+ move-result v0
+ return v0
+.end method
+
+
diff --git a/test/633-checker-rtp-getclass/src/Main.java b/test/633-checker-rtp-getclass/src/Main.java
index f29c139f63..d1145af2a5 100644
--- a/test/633-checker-rtp-getclass/src/Main.java
+++ b/test/633-checker-rtp-getclass/src/Main.java
@@ -14,34 +14,13 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
public static void main(String[] args) {
- System.out.println($opt$noinline$foo(new Main()));
- System.out.println($opt$noinline$foo(new SubMain()));
- System.out.println($opt$noinline$foo(new SubSubMain()));
- }
-
-
- // Checker test to make sure the only inlined instruction is
- // SubMain.bar.
- /// CHECK-START: int Main.$opt$noinline$foo(Main) inliner (after)
- /// CHECK-DAG: InvokeVirtual method_name:Main.foo
- /// CHECK-DAG: <<Const:i\d+>> IntConstant 3
- /// CHECK: begin_block
- /// CHECK: BoundType klass:SubMain
- /// CHECK: Return [<<Const>>]
- /// CHECK-NOT: begin_block
- /// CHECK: end_block
- public static int $opt$noinline$foo(Main o) {
- if (doThrow) { throw new Error(); }
- // To exercise the bug on Jack, we need two getClass compares.
- if (o.getClass() == Main.class || o.getClass() != SubMain.class) {
- return o.foo();
- } else {
- // We used to wrongly bound the type of o to `Main` here and then realize that's
- // impossible and mark this branch as dead.
- return o.bar();
- }
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new Main()));
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new SubMain()));
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new SubSubMain()));
}
public int bar() {
@@ -53,6 +32,16 @@ public class Main {
}
public static boolean doThrow = false;
+
+ public static int $noinline$runSmaliTest(String name, Main input) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, Main.class);
+ return (Integer) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
}
class SubMain extends Main {
diff --git a/test/640-checker-boolean-simd/src/Main.java b/test/640-checker-boolean-simd/src/Main.java
index 64b76f8516..c337ef4fed 100644
--- a/test/640-checker-boolean-simd/src/Main.java
+++ b/test/640-checker-boolean-simd/src/Main.java
@@ -30,6 +30,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.and(boolean) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAnd loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.and(boolean) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -51,6 +57,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.or(boolean) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecOr loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.or(boolean) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +84,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.xor(boolean) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecXor loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.xor(boolean) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -93,6 +111,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.not() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.not() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/640-checker-byte-simd/src/Main.java b/test/640-checker-byte-simd/src/Main.java
index 283c2c907d..dc7aaf7f05 100644
--- a/test/640-checker-byte-simd/src/Main.java
+++ b/test/640-checker-byte-simd/src/Main.java
@@ -30,6 +30,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.add(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -51,6 +57,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +84,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -107,6 +125,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.neg() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -128,6 +152,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.not() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.not() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -149,6 +179,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shl4() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -170,6 +206,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.sar2() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/640-checker-char-simd/src/Main.java b/test/640-checker-char-simd/src/Main.java
index dd879b40cd..0ba596389d 100644
--- a/test/640-checker-char-simd/src/Main.java
+++ b/test/640-checker-char-simd/src/Main.java
@@ -30,6 +30,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.add(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -51,6 +57,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +84,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -107,6 +125,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.neg() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -128,6 +152,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.not() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.not() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -149,6 +179,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shl4() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -183,6 +219,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shr2() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/640-checker-int-simd/src/Main.java b/test/640-checker-int-simd/src/Main.java
index 9abf60d6fa..10dd340129 100644
--- a/test/640-checker-int-simd/src/Main.java
+++ b/test/640-checker-int-simd/src/Main.java
@@ -30,6 +30,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.add(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -51,6 +57,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +84,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -108,6 +126,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.neg() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -129,6 +153,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.not() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.not() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -150,6 +180,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shl4() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -170,7 +206,13 @@ public class Main {
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
+ //
+ /// CHECK-START-ARM: void Main.sar2() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -192,6 +234,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shr2() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecUShr loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -229,6 +277,11 @@ public class Main {
/// CHECK-DAG: <<Get:i\d+>> ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<Get>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shr32() loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Get>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shr32() loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -258,6 +311,13 @@ public class Main {
/// CHECK-DAG: <<UShr:i\d+>> UShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shr33() loop_optimization (after)
+ /// CHECK-DAG: <<Dist:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<UShr>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shr33() loop_optimization (after)
/// CHECK-DAG: <<Dist:i\d+>> IntConstant 1 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
@@ -290,6 +350,13 @@ public class Main {
/// CHECK-DAG: <<UShr:i\d+>> UShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<UShr>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shrMinus254() loop_optimization (after)
+ /// CHECK-DAG: <<Dist:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<UShr:d\d+>> VecUShr [<<Get>>,<<Dist>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<UShr>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after)
/// CHECK-DAG: <<Dist:i\d+>> IntConstant 2 loop:none
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
diff --git a/test/640-checker-short-simd/src/Main.java b/test/640-checker-short-simd/src/Main.java
index 4cca837efb..9dc084d1df 100644
--- a/test/640-checker-short-simd/src/Main.java
+++ b/test/640-checker-short-simd/src/Main.java
@@ -30,6 +30,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.add(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecAdd loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -51,6 +57,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecSub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +84,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecMul loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -107,6 +125,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.neg() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNeg loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -128,6 +152,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.not() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecNot loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.not() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -149,6 +179,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.shl4() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShl loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
@@ -170,6 +206,12 @@ public class Main {
/// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.sar2() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecShr loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java
index 9714a46630..c49d85d33f 100644
--- a/test/645-checker-abs-simd/src/Main.java
+++ b/test/645-checker-abs-simd/src/Main.java
@@ -28,6 +28,18 @@ public class Main {
/// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitByte(byte[]) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none
+ //
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
/// CHECK-START-ARM64: void Main.doitByte(byte[]) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop1>> outer_loop:none
@@ -78,6 +90,18 @@ public class Main {
/// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitShort(short[]) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none
+ //
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
/// CHECK-START-ARM64: void Main.doitShort(short[]) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop1>> outer_loop:none
@@ -113,6 +137,18 @@ public class Main {
/// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitInt(int[]) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: VecLoad loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecAbs loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: VecStore loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop2>> outer_loop:none
+ //
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
/// CHECK-START-ARM64: void Main.doitInt(int[]) loop_optimization (after)
/// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
/// CHECK-DAG: VecLoad loop:<<Loop1>> outer_loop:none
diff --git a/test/646-checker-hadd-alt-byte/src/Main.java b/test/646-checker-hadd-alt-byte/src/Main.java
index 9cc68287c3..7be3151c6c 100644
--- a/test/646-checker-hadd-alt-byte/src/Main.java
+++ b/test/646-checker-hadd-alt-byte/src/Main.java
@@ -39,6 +39,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +79,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -103,6 +117,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -137,6 +158,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -167,6 +195,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none
@@ -200,6 +236,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none
diff --git a/test/646-checker-hadd-alt-char/src/Main.java b/test/646-checker-hadd-alt-char/src/Main.java
index 3f81299476..2799ea7483 100644
--- a/test/646-checker-hadd-alt-char/src/Main.java
+++ b/test/646-checker-hadd-alt-char/src/Main.java
@@ -39,6 +39,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +79,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -106,6 +120,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -140,6 +161,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -173,6 +201,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
diff --git a/test/646-checker-hadd-alt-short/src/Main.java b/test/646-checker-hadd-alt-short/src/Main.java
index 150626cdd7..6cd102f035 100644
--- a/test/646-checker-hadd-alt-short/src/Main.java
+++ b/test/646-checker-hadd-alt-short/src/Main.java
@@ -39,6 +39,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -72,6 +79,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -103,6 +117,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -137,6 +158,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -167,6 +195,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none
@@ -200,6 +236,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<UShr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
diff --git a/test/646-checker-hadd-byte/src/Main.java b/test/646-checker-hadd-byte/src/Main.java
index 5a615a429e..a9e844cf17 100644
--- a/test/646-checker-hadd-byte/src/Main.java
+++ b/test/646-checker-hadd-byte/src/Main.java
@@ -36,6 +36,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -69,6 +76,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -100,6 +114,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -134,6 +155,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -164,6 +192,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<I127:i\d+>> IntConstant 127 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I127>>] loop:none
@@ -197,6 +233,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<I255:i\d+>> IntConstant 255 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<I255>>] loop:none
diff --git a/test/646-checker-hadd-char/src/Main.java b/test/646-checker-hadd-char/src/Main.java
index bb8a01f2cc..22eb7cb3cc 100644
--- a/test/646-checker-hadd-char/src/Main.java
+++ b/test/646-checker-hadd-char/src/Main.java
@@ -36,6 +36,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -69,6 +76,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -103,6 +117,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -137,6 +158,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -170,6 +198,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
@@ -203,6 +239,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java
index 07845a6038..756f8a8ae9 100644
--- a/test/646-checker-hadd-short/src/Main.java
+++ b/test/646-checker-hadd-short/src/Main.java
@@ -36,6 +36,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -70,6 +77,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -104,6 +118,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -135,6 +156,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -166,6 +194,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -201,6 +236,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:false rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -236,6 +278,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -270,6 +319,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get1>>,<<Get2>>] unsigned:true rounded:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -301,6 +357,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:false rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<SMAX:i\d+>> IntConstant 32767 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<SMAX>>] loop:none
@@ -334,6 +398,14 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Shr>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<HAdd:d\d+>> VecHalvingAdd [<<Get>>,<<Repl>>] unsigned:true rounded:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<HAdd>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<UMAX:i\d+>> IntConstant 65535 loop:none
/// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<UMAX>>] loop:none
diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-byte-simd-minmax/src/Main.java
index 4711214c9d..e018b56dd5 100644
--- a/test/651-checker-byte-simd-minmax/src/Main.java
+++ b/test/651-checker-byte-simd-minmax/src/Main.java
@@ -27,6 +27,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -58,6 +65,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -86,6 +100,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -117,6 +138,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:b\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-char-simd-minmax/src/Main.java
index 79795ee0bd..57cad9b34a 100644
--- a/test/651-checker-char-simd-minmax/src/Main.java
+++ b/test/651-checker-char-simd-minmax/src/Main.java
@@ -27,6 +27,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMin(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMin(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -55,6 +62,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMax(char[], char[], char[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMax(char[], char[], char[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-int-simd-minmax/src/Main.java
index 2a97009ae9..11b67b84d3 100644
--- a/test/651-checker-int-simd-minmax/src/Main.java
+++ b/test/651-checker-int-simd-minmax/src/Main.java
@@ -26,6 +26,13 @@ public class Main {
/// CHECK-DAG: <<Min:i\d+>> InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMin(int[], int[], int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMin(int[], int[], int[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -53,6 +60,13 @@ public class Main {
/// CHECK-DAG: <<Max:i\d+>> InvokeStaticOrDirect [<<Get1>>,<<Get2>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMax(int[], int[], int[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMax(int[], int[], int[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-short-simd-minmax/src/Main.java
index 3bd1305e3e..4f2a7a4440 100644
--- a/test/651-checker-short-simd-minmax/src/Main.java
+++ b/test/651-checker-short-simd-minmax/src/Main.java
@@ -27,6 +27,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMin(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMin(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -58,6 +65,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Min:d\d+>> VecMin [<<Get1>>,<<Get2>>] unsigned:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Min>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -86,6 +100,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMax(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:false loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMax(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
@@ -117,6 +138,13 @@ public class Main {
/// CHECK-DAG: <<Cnv:s\d+>> TypeConversion [<<Max>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Cnv>>] loop:<<Loop>> outer_loop:none
//
+ /// CHECK-START-ARM: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Max:d\d+>> VecMax [<<Get1>>,<<Get2>>] unsigned:true loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Max>>] loop:<<Loop>> outer_loop:none
+ //
/// CHECK-START-ARM64: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Get1:d\d+>> VecLoad loop:<<Loop>> outer_loop:none
diff --git a/test/652-deopt-intrinsic/run b/test/652-deopt-intrinsic/run
new file mode 100755
index 0000000000..97d1ff16bb
--- /dev/null
+++ b/test/652-deopt-intrinsic/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Ensure this test is not subject to code collection.
+exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M
diff --git a/test/656-checker-simd-opt/expected.txt b/test/656-checker-simd-opt/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/656-checker-simd-opt/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/656-checker-simd-opt/info.txt b/test/656-checker-simd-opt/info.txt
new file mode 100644
index 0000000000..185d2b1b95
--- /dev/null
+++ b/test/656-checker-simd-opt/info.txt
@@ -0,0 +1 @@
+Tests around optimizations of SIMD code.
diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java
new file mode 100644
index 0000000000..794c9b6c0d
--- /dev/null
+++ b/test/656-checker-simd-opt/src/Main.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Tests for SIMD related optimizations.
+ */
+public class Main {
+
+ /// CHECK-START: void Main.unroll(float[], float[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Get>>,<<Cons>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Mul>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.unroll(float[], float[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5 loop:none
+ /// CHECK-DAG: <<Incr:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Cons>>] loop:none
+ /// CHECK-NOT: VecReplicateScalar
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul1:d\d+>> VecMul [<<Get1>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Mul1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Phi>>,<<Incr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul2:d\d+>> VecMul [<<Get2>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Add>>,<<Mul2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Add>>,<<Incr>>] loop:<<Loop>> outer_loop:none
+ private static void unroll(float[] x, float[] y) {
+ for (int i = 0; i < 100; i++) {
+ x[i] = y[i] * 2.5f;
+ }
+ }
+
+ /// CHECK-START: void Main.stencil(int[], int[], int) loop_optimization (before)
+ /// CHECK-DAG: <<CP1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<CM1:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Phi>>,<<CM1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [{{l\d+}},<<Add1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:i\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Phi>>,<<CP1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get3:i\d+>> ArrayGet [{{l\d+}},<<Add3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add4:i\d+>> Add [<<Add2>>,<<Get3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Add4>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.stencil(int[], int[], int) loop_optimization (after)
+ /// CHECK-DAG: <<CP1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<CP2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Phi>>,<<CP1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Add1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:d\d+>> VecAdd [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Phi>>,<<CP2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get3:d\d+>> VecLoad [{{l\d+}},<<Add3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add4:d\d+>> VecAdd [<<Add2>>,<<Get3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Add1>>,<<Add4>>] loop:<<Loop>> outer_loop:none
+ private static void stencil(int[] a, int[] b, int n) {
+ for (int i = 1; i < n - 1; i++) {
+ a[i] = b[i - 1] + b[i] + b[i + 1];
+ }
+ }
+
+ public static void main(String[] args) {
+ float[] x = new float[100];
+ float[] y = new float[100];
+ for (int i = 0; i < 100; i++) {
+ x[i] = 0.0f;
+ y[i] = 2.0f;
+ }
+ unroll(x, y);
+ for (int i = 0; i < 100; i++) {
+ expectEquals(5.0f, x[i]);
+ expectEquals(2.0f, y[i]);
+ }
+ int[] a = new int[100];
+ int[] b = new int[100];
+ for (int i = 0; i < 100; i++) {
+ a[i] = 0;
+ b[i] = i;
+ }
+ stencil(a, b, 100);
+ for (int i = 1; i < 99; i++) {
+ int e = i + i + i;
+ expectEquals(e, a[i]);
+ expectEquals(i, b[i]);
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/658-fp-read-barrier/expected.txt b/test/658-fp-read-barrier/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/658-fp-read-barrier/expected.txt
diff --git a/test/658-fp-read-barrier/info.txt b/test/658-fp-read-barrier/info.txt
new file mode 100644
index 0000000000..26ecb60aaf
--- /dev/null
+++ b/test/658-fp-read-barrier/info.txt
@@ -0,0 +1,2 @@
+Regression test for the read barrier implementation in ARM64,
+which used to not restore floating point registers.
diff --git a/test/658-fp-read-barrier/src/Main.java b/test/658-fp-read-barrier/src/Main.java
new file mode 100644
index 0000000000..eed3c6116b
--- /dev/null
+++ b/test/658-fp-read-barrier/src/Main.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 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 {
+ static volatile boolean done = false;
+
+ public static void main(String[] args) {
+ // Run a thread for 30 seconds, allocating memory and triggering garbage
+ // collection.
+ // Time is limited to 30 seconds to not make this test too long. The test used
+ // to trigger the failure around 1 every 10 runs.
+ Thread t = new Thread() {
+ public void run() {
+ long time = System.currentTimeMillis();
+ while (System.currentTimeMillis() - time < 30000) {
+ for (int j = 0; j < 10000; j++) {
+ o = new Object[1000];
+ }
+ Runtime.getRuntime().gc();
+ Thread.yield();
+ }
+ Main.done = true;
+ }
+ Object o;
+ };
+ // Make the thread a daemon to quit early in case of an
+ // exception thrown below.
+ t.setDaemon(true);
+ t.start();
+
+ // Run 'foo' as long as the test runs.
+ while (!done) {
+ double res = foo(staticMain);
+ if (res != 529.0) {
+ throw new Error("Unexpected result " + res);
+ }
+ }
+ }
+
+ public static double foo(Main main) {
+ // Use up all D registers on arm64.
+ double d1 = main.field1;
+ double d2 = main.field2;
+ double d3 = main.field3;
+ double d4 = main.field4;
+ double d5 = main.field5;
+ double d6 = main.field6;
+ double d7 = main.field7;
+ double d8 = main.field8;
+ double d9 = main.field9;
+ double d10 = main.field10;
+ double d11 = main.field11;
+ double d12 = main.field12;
+ double d13 = main.field13;
+ double d14 = main.field14;
+ double d15 = main.field15;
+ double d16 = main.field16;
+ double d17 = main.field17;
+ double d18 = main.field18;
+ double d19 = main.field19;
+ double d20 = main.field20;
+ double d21 = main.field21;
+ double d22 = main.field22;
+ double d23 = main.field23;
+ double d24 = main.field24;
+ double d25 = main.field25;
+ double d26 = main.field26;
+ double d27 = main.field27;
+ double d28 = main.field28;
+ double d29 = main.field29;
+ double d30 = main.field30;
+ double d31 = main.field31;
+ double d32 = main.field32;
+
+ // Trigger a read barrier. This used to make the test trip on ARM64 as
+ // the read barrier stub used to not restore the D registers.
+ double p = main.objectField.field1;
+
+ return p + 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 + d32;
+ }
+
+ // Initialize objects here and not in 'main' to avoid having
+ // these objects in roots.
+ public static Main staticMain = new Main();
+ static {
+ staticMain.objectField = new Main();
+ }
+
+ public Main objectField;
+
+ public double field1 = 1.0;
+ public double field2 = 2.0;
+ public double field3 = 3.0;
+ public double field4 = 4.0;
+ public double field5 = 5.0;
+ public double field6 = 6.0;
+ public double field7 = 7.0;
+ public double field8 = 8.0;
+ public double field9 = 9.0;
+ public double field10 = 10.0;
+ public double field11 = 11.0;
+ public double field12 = 12.0;
+ public double field13 = 13.0;
+ public double field14 = 14.0;
+ public double field15 = 15.0;
+ public double field16 = 16.0;
+ public double field17 = 17.0;
+ public double field18 = 18.0;
+ public double field19 = 19.0;
+ public double field20 = 20.0;
+ public double field21 = 21.0;
+ public double field22 = 22.0;
+ public double field23 = 23.0;
+ public double field24 = 24.0;
+ public double field25 = 25.0;
+ public double field26 = 26.0;
+ public double field27 = 27.0;
+ public double field28 = 28.0;
+ public double field29 = 29.0;
+ public double field30 = 30.0;
+ public double field31 = 31.0;
+ public double field32 = 32.0;
+}
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index 1721e4294e..a68565b552 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -16,8 +16,22 @@
public class Main {
+ public class ExampleObj {
+ int n1;
+ int n2;
+
+ public ExampleObj(int n1, int n2) {
+ this.n1 = n1;
+ this.n2 = n2;
+ }
+ }
+
static int static_variable = 0;
+ public ExampleObj my_obj;
+ public static int number1;
+ public static int number2;
+
/// CHECK-START-ARM64: int Main.arrayAccess() scheduler (before)
/// CHECK: <<Const1:i\d+>> IntConstant 1
/// CHECK: <<i0:i\d+>> Phi
@@ -50,6 +64,282 @@ public class Main {
return res;
}
+ /// CHECK-START-ARM: void Main.arrayAccessVariable(int) scheduler (before)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const1>>]
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const1>>]
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
+ /// CHECK: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const1>>]
+ /// CHECK: <<ArraySet3:v\d+>> ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
+
+ /// CHECK-START-ARM: void Main.arrayAccessVariable(int) scheduler (after)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+
+ /// CHECK-START-ARM64: void Main.arrayAccessVariable(int) scheduler (before)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const1>>]
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const1>>]
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
+ /// CHECK: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const1>>]
+ /// CHECK: <<ArraySet3:v\d+>> ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
+
+ /// CHECK-START-ARM64: void Main.arrayAccessVariable(int) scheduler (after)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ public static void arrayAccessVariable(int i) {
+ int [] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (int j = 0; j < 100; j++) {
+ array[i + 1]++;
+ array[i + 2]++;
+ array[i - 1]++;
+ }
+ }
+
+ /// CHECK-START-ARM: void Main.arrayAccessSub(int) scheduler (before)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 9
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Sub2:i\d+>> Sub [<<Const2>>,<<Param>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const3>>]
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Sub2>>]
+ /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const3>>]
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Sub2>>,<<AddArray2>>]
+
+ /// CHECK-START-ARM: void Main.arrayAccessSub(int) scheduler (after)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 9
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Sub2:i\d+>> Sub [<<Const2>>,<<Param>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const3>>]
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Sub2>>]
+ /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const3>>]
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Sub2>>,<<AddArray2>>]
+
+ /// CHECK-START-ARM64: void Main.arrayAccessSub(int) scheduler (before)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 9
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Sub2:i\d+>> Sub [<<Const2>>,<<Param>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const3>>]
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Sub2>>]
+ /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const3>>]
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Sub2>>,<<AddArray2>>]
+
+ /// CHECK-START-ARM64: void Main.arrayAccessSub(int) scheduler (after)
+ /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 9
+ /// CHECK-DAG: <<Const3:i\d+>> IntConstant 1
+ /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK: <<Sub2:i\d+>> Sub [<<Const2>>,<<Param>>]
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const3>>]
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Sub2>>]
+ /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const3>>]
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Sub2>>,<<AddArray2>>]
+ public static void arrayAccessSub(int i) {
+ int [] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (int j = 0; j < 100; j++) {
+ // These two accesses MAY ALIAS
+ array[i - 1]++;
+ array[9 - i]++;
+ }
+ }
+
+ /// CHECK-START-ARM: void Main.arrayAccessLoopVariable() scheduler (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Phi:i\d+>> Phi
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet
+ /// CHECK: <<AddArray1:i\d+>> Add
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet
+ /// CHECK: <<AddVar:i\d+>> Add
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet
+ /// CHECK: <<AddArray2:i\d+>> Add
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet
+
+ /// CHECK-START-ARM: void Main.arrayAccessLoopVariable() scheduler (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Phi:i\d+>> Phi
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<AddVar:i\d+>> Add
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet
+ /// CHECK: <<AddArray1:i\d+>> Add
+ /// CHECK: <<AddArray2:i\d+>> Add
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet
+
+ /// CHECK-START-ARM64: void Main.arrayAccessLoopVariable() scheduler (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Phi:i\d+>> Phi
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet
+ /// CHECK: <<AddArray1:i\d+>> Add
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet
+ /// CHECK: <<AddVar:i\d+>> Add
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet
+ /// CHECK: <<AddArray2:i\d+>> Add
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet
+
+ /// CHECK-START-ARM64: void Main.arrayAccessLoopVariable() scheduler (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK: <<Phi:i\d+>> Phi
+ /// CHECK: <<Array:i\d+>> IntermediateAddress
+ /// CHECK: <<AddVar:i\d+>> Add
+ /// CHECK: <<ArrayGet1:i\d+>> ArrayGet
+ /// CHECK: <<ArrayGet2:i\d+>> ArrayGet
+ /// CHECK: <<AddArray1:i\d+>> Add
+ /// CHECK: <<AddArray2:i\d+>> Add
+ /// CHECK: <<ArraySet1:v\d+>> ArraySet
+ /// CHECK: <<ArraySet2:v\d+>> ArraySet
+ public static void arrayAccessLoopVariable() {
+ int [] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ for (int j = 0; j < 9; j++) {
+ array[j]++;
+ array[j + 1]++;
+ }
+ }
+
+ /// CHECK-START-ARM: void Main.accessFields() scheduler (before)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+
+ /// CHECK-START-ARM: void Main.accessFields() scheduler (after)
+ /// CHECK-DAG: InstanceFieldGet
+ /// CHECK-DAG: InstanceFieldGet
+ /// CHECK-DAG: StaticFieldGet
+ /// CHECK-DAG: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: StaticFieldSet
+ /// CHECK-DAG: StaticFieldSet
+
+ /// CHECK-START-ARM64: void Main.accessFields() scheduler (before)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: Add
+ /// CHECK: InstanceFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+ /// CHECK: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: StaticFieldSet
+
+ /// CHECK-START-ARM64: void Main.accessFields() scheduler (after)
+ /// CHECK-DAG: InstanceFieldGet
+ /// CHECK-DAG: InstanceFieldGet
+ /// CHECK-DAG: StaticFieldGet
+ /// CHECK-DAG: StaticFieldGet
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: StaticFieldSet
+ /// CHECK-DAG: StaticFieldSet
+ public void accessFields() {
+ my_obj = new ExampleObj(1, 2);
+ for (int i = 0; i < 10; i++) {
+ my_obj.n1++;
+ my_obj.n2++;
+ number1++;
+ number2++;
+ }
+ }
+
/// CHECK-START-ARM64: int Main.intDiv(int) scheduler (before)
/// CHECK: Sub
/// CHECK: DivZeroCheck
diff --git a/test/952-invoke-custom-kinds/build b/test/952-invoke-custom-kinds/build
new file mode 100644
index 0000000000..a02cdc3769
--- /dev/null
+++ b/test/952-invoke-custom-kinds/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Stop if something fails.
+set -e
+
+${DX} --dex --min-sdk-version=26 --output=classes.dex classes
+
+zip $TEST_NAME.jar classes.dex
diff --git a/test/952-invoke-custom-kinds/classes/Main.class b/test/952-invoke-custom-kinds/classes/Main.class
new file mode 100644
index 0000000000..6bc04e326a
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/Main.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class
new file mode 100644
index 0000000000..5dfe958795
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/Interface.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class
new file mode 100644
index 0000000000..a11ee696bf
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InterfaceImplementor.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class
new file mode 100644
index 0000000000..e233febbf4
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$Interface.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class
new file mode 100644
index 0000000000..41e1d431f2
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom$InterfaceImplementor.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
new file mode 100644
index 0000000000..4f0f497c72
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/InvokeCustom.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/Super.class b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
new file mode 100644
index 0000000000..5990f28d47
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/Super.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class
new file mode 100644
index 0000000000..c3266e49f7
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator$1.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
new file mode 100644
index 0000000000..711db2343b
--- /dev/null
+++ b/test/952-invoke-custom-kinds/classes/invokecustom/TestGenerator.class
Binary files differ
diff --git a/test/952-invoke-custom-kinds/expected.txt b/test/952-invoke-custom-kinds/expected.txt
new file mode 100644
index 0000000000..c72da25c99
--- /dev/null
+++ b/test/952-invoke-custom-kinds/expected.txt
@@ -0,0 +1,38 @@
+bsmLookupStatic []
+Hello World!
+bsmLookupStatic []
+true
+127
+c
+1024
+123456
+1.2
+123456789
+3.5123456789
+String
+bsmLookupStaticWithExtraArgs [1, 123456789, 123.456, 123456.789123]
+targetMethodTest3 from InvokeCustom
+bsmCreateCallSite [MethodHandle(Super)void]
+targetMethodTest4 from Super
+bsmLookupStatic []
+targetMethodTest5 1000 + -923 = 77
+targetMethodTest5 returned: 77
+bsmLookupStatic []
+targetMethodTest6 8209686820727 + -1172812402961 = 7036874417766
+targetMethodTest6 returned: 7036874417766
+bsmLookupStatic []
+targetMethodTest7 0.50097656 * -0.50097656 = -0.2509775161743164
+targetMethodTest6 returned: -0.2509775161743164
+bsmLookupStatic []
+targetMethodTest8 First invokedynamic invocation
+bsmLookupStatic []
+targetMethodTest8 Second invokedynamic invocation
+bsmLookupStatic []
+targetMethodTest8 Dupe first invokedynamic invocation
+bsmLookupTest9 [MethodHandle()int, MethodHandle(int)void, MethodHandle(InvokeCustom)float, MethodHandle(InvokeCustom,float)void]
+targetMethodTest9 ()void
+checkStaticFieldTest9: old 0 new 1985229328 expected 1985229328 OK
+checkFieldTest9: old 0.0 new 1.99E-19 expected 1.99E-19 OK
+helperMethodTest9 in class invokecustom.InvokeCustom
+run() for Test9
+targetMethodTest9()
diff --git a/test/952-invoke-custom-kinds/info.txt b/test/952-invoke-custom-kinds/info.txt
new file mode 100644
index 0000000000..cddb96dcf0
--- /dev/null
+++ b/test/952-invoke-custom-kinds/info.txt
@@ -0,0 +1,7 @@
+This test checks call sites and constant method handles in DEX files used
+by invoke-custom.
+
+It is derived from a dx test (dalvik/dx/tests/135-invoke-custom) which
+generates the invoke-custom using ASM to generate class files. The
+test here differs as it not include an instance a constant method
+handle with a constructor kind (see b/62774190).
diff --git a/test/988-method-trace/check b/test/988-method-trace/check
new file mode 100644
index 0000000000..4c583eb051
--- /dev/null
+++ b/test/988-method-trace/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+# Jack uses @hide API which gives it wrong method trace in the expected.txt
+if [[ "$USE_JACK" == true ]]; then
+ patch -p0 expected.txt < expected_jack.diff >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 30ad532f6c..574d5b0772 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -1,4 +1,4 @@
-.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+.<= public static void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
<= public static void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
@@ -130,8 +130,8 @@ fibonacci(5)=5
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -140,14 +140,14 @@ fibonacci(5)=5
.....=> public static final java.util.List java.util.Collections.emptyList()
.....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
.....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
-......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
-......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:209)
- at art.Test988$IterOp.applyAsInt(Test988.java:204)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:267)
- at Main.main(Main.java:19)
+ art.Test988.iter_fibonacci(Test988.java:235)
+ art.Test988$IterOp.applyAsInt(Test988.java:230)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:304)
+ <additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
...<= public java.lang.Error(java.lang.String) -> <null: null>
@@ -163,11 +163,11 @@ fibonacci(5)=5
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:209)
- at art.Test988$IterOp.applyAsInt(Test988.java:204)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:267)
- at Main.main(Main.java:19)
+ art.Test988.iter_fibonacci(Test988.java:235)
+ art.Test988$IterOp.applyAsInt(Test988.java:230)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:304)
+ <additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
@@ -231,8 +231,8 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-....<= static native java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
+....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -241,14 +241,14 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
.....=> public static final java.util.List java.util.Collections.emptyList()
.....<= public static final java.util.List java.util.Collections.emptyList() -> <class java.util.Collections$EmptyList: []>
.....=> public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
-......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
-......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
+......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
+......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:231)
- at art.Test988$RecurOp.applyAsInt(Test988.java:226)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:268)
- at Main.main(Main.java:19)
+ art.Test988.fibonacci(Test988.java:257)
+ art.Test988$RecurOp.applyAsInt(Test988.java:252)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:305)
+ <additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
...<= public java.lang.Error(java.lang.String) -> <null: null>
@@ -264,14 +264,194 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:231)
- at art.Test988$RecurOp.applyAsInt(Test988.java:226)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:268)
- at Main.main(Main.java:19)
+ art.Test988.fibonacci(Test988.java:257)
+ art.Test988$RecurOp.applyAsInt(Test988.java:252)
+ art.Test988.doFibTest(Test988.java:339)
+ art.Test988.run(Test988.java:305)
+ <additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
-=> public static native java.lang.Thread java.lang.Thread.currentThread()
-<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <class java.lang.Thread: <non-deterministic>>
-=> public static native void art.Trace.disableTracing(java.lang.Thread)
+=> static void art.Test988$IntrinsicsTest.doTest()
+.=> static void art.Test988Intrinsics.test()
+..=> public static long java.lang.Double.doubleToRawLongBits(double)
+..<= public static long java.lang.Double.doubleToRawLongBits(double) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Double.doubleToLongBits(double)
+..<= public static long java.lang.Double.doubleToLongBits(double) -> <class java.lang.Long: 0>
+..=> public static boolean java.lang.Double.isInfinite(double)
+..<= public static boolean java.lang.Double.isInfinite(double) -> <class java.lang.Boolean: false>
+..=> public static boolean java.lang.Double.isNaN(double)
+..<= public static boolean java.lang.Double.isNaN(double) -> <class java.lang.Boolean: false>
+..=> public static double java.lang.Double.longBitsToDouble(long)
+..<= public static double java.lang.Double.longBitsToDouble(long) -> <class java.lang.Double: 0.0>
+..=> public static int java.lang.Float.floatToRawIntBits(float)
+..<= public static int java.lang.Float.floatToRawIntBits(float) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Float.floatToIntBits(float)
+..<= public static int java.lang.Float.floatToIntBits(float) -> <class java.lang.Integer: 0>
+..=> public static boolean java.lang.Float.isInfinite(float)
+..<= public static boolean java.lang.Float.isInfinite(float) -> <class java.lang.Boolean: false>
+..=> public static boolean java.lang.Float.isNaN(float)
+..<= public static boolean java.lang.Float.isNaN(float) -> <class java.lang.Boolean: false>
+..=> public static float java.lang.Float.intBitsToFloat(int)
+..<= public static float java.lang.Float.intBitsToFloat(int) -> <class java.lang.Float: 0.0>
+..=> public static int java.lang.Integer.reverse(int)
+..<= public static int java.lang.Integer.reverse(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.reverseBytes(int)
+..<= public static int java.lang.Integer.reverseBytes(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.bitCount(int)
+..<= public static int java.lang.Integer.bitCount(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.compare(int,int)
+..<= public static int java.lang.Integer.compare(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.highestOneBit(int)
+..<= public static int java.lang.Integer.highestOneBit(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.lowestOneBit(int)
+..<= public static int java.lang.Integer.lowestOneBit(int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.numberOfLeadingZeros(int)
+..<= public static int java.lang.Integer.numberOfLeadingZeros(int) -> <class java.lang.Integer: 32>
+..=> public static int java.lang.Integer.numberOfTrailingZeros(int)
+..<= public static int java.lang.Integer.numberOfTrailingZeros(int) -> <class java.lang.Integer: 32>
+..=> public static int java.lang.Integer.rotateRight(int,int)
+..<= public static int java.lang.Integer.rotateRight(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.rotateLeft(int,int)
+..<= public static int java.lang.Integer.rotateLeft(int,int) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Integer.signum(int)
+..<= public static int java.lang.Integer.signum(int) -> <class java.lang.Integer: 0>
+..=> public static long java.lang.Long.reverse(long)
+..<= public static long java.lang.Long.reverse(long) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.reverseBytes(long)
+..<= public static long java.lang.Long.reverseBytes(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.bitCount(long)
+..<= public static int java.lang.Long.bitCount(long) -> <class java.lang.Integer: 0>
+..=> public static int java.lang.Long.compare(long,long)
+..<= public static int java.lang.Long.compare(long,long) -> <class java.lang.Integer: 0>
+..=> public static long java.lang.Long.highestOneBit(long)
+..<= public static long java.lang.Long.highestOneBit(long) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.lowestOneBit(long)
+..<= public static long java.lang.Long.lowestOneBit(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.numberOfLeadingZeros(long)
+..<= public static int java.lang.Long.numberOfLeadingZeros(long) -> <class java.lang.Integer: 64>
+..=> public static int java.lang.Long.numberOfTrailingZeros(long)
+..<= public static int java.lang.Long.numberOfTrailingZeros(long) -> <class java.lang.Integer: 64>
+..=> public static long java.lang.Long.rotateRight(long,int)
+..<= public static long java.lang.Long.rotateRight(long,int) -> <class java.lang.Long: 0>
+..=> public static long java.lang.Long.rotateLeft(long,int)
+..<= public static long java.lang.Long.rotateLeft(long,int) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Long.signum(long)
+..<= public static int java.lang.Long.signum(long) -> <class java.lang.Integer: 0>
+..=> public static short java.lang.Short.reverseBytes(short)
+..<= public static short java.lang.Short.reverseBytes(short) -> <class java.lang.Short: 0>
+..=> public static double java.lang.Math.abs(double)
+..<= public static double java.lang.Math.abs(double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.abs(float)
+..<= public static float java.lang.Math.abs(float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.abs(long)
+..<= public static long java.lang.Math.abs(long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.abs(int)
+..<= public static int java.lang.Math.abs(int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.min(double,double)
+..<= public static double java.lang.Math.min(double,double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.min(float,float)
+..<= public static float java.lang.Math.min(float,float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.min(long,long)
+..<= public static long java.lang.Math.min(long,long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.min(int,int)
+..<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.max(double,double)
+..<= public static double java.lang.Math.max(double,double) -> <class java.lang.Double: 0.0>
+..=> public static float java.lang.Math.max(float,float)
+..<= public static float java.lang.Math.max(float,float) -> <class java.lang.Float: 0.0>
+..=> public static long java.lang.Math.max(long,long)
+..<= public static long java.lang.Math.max(long,long) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.max(int,int)
+..<= public static int java.lang.Math.max(int,int) -> <class java.lang.Integer: 0>
+..=> public static double java.lang.Math.cos(double)
+..<= public static double java.lang.Math.cos(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.sin(double)
+..<= public static double java.lang.Math.sin(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.acos(double)
+..<= public static double java.lang.Math.acos(double) -> <class java.lang.Double: 1.5707963267948966>
+..=> public static double java.lang.Math.asin(double)
+..<= public static double java.lang.Math.asin(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.atan(double)
+..<= public static double java.lang.Math.atan(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.atan2(double,double)
+..<= public static double java.lang.Math.atan2(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.cbrt(double)
+..<= public static double java.lang.Math.cbrt(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.cosh(double)
+..<= public static double java.lang.Math.cosh(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.exp(double)
+..<= public static double java.lang.Math.exp(double) -> <class java.lang.Double: 1.0>
+..=> public static double java.lang.Math.expm1(double)
+..<= public static double java.lang.Math.expm1(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.hypot(double,double)
+..<= public static double java.lang.Math.hypot(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.log(double)
+..<= public static double java.lang.Math.log(double) -> <class java.lang.Double: -Infinity>
+..=> public static double java.lang.Math.log10(double)
+..<= public static double java.lang.Math.log10(double) -> <class java.lang.Double: -Infinity>
+..=> public static double java.lang.Math.nextAfter(double,double)
+..<= public static double java.lang.Math.nextAfter(double,double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.sinh(double)
+..<= public static double java.lang.Math.sinh(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.tan(double)
+..<= public static double java.lang.Math.tan(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.tanh(double)
+..<= public static double java.lang.Math.tanh(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.sqrt(double)
+..<= public static double java.lang.Math.sqrt(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.ceil(double)
+..<= public static double java.lang.Math.ceil(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.floor(double)
+..<= public static double java.lang.Math.floor(double) -> <class java.lang.Double: 0.0>
+..=> public static double java.lang.Math.rint(double)
+..<= public static double java.lang.Math.rint(double) -> <class java.lang.Double: 0.0>
+..=> public static long java.lang.Math.round(double)
+..<= public static long java.lang.Math.round(double) -> <class java.lang.Long: 0>
+..=> public static int java.lang.Math.round(float)
+..<= public static int java.lang.Math.round(float) -> <class java.lang.Integer: 0>
+..=> public static java.lang.Thread java.lang.Thread.currentThread()
+..<= public static java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
+..=> public char java.lang.String.charAt(int)
+..<= public char java.lang.String.charAt(int) -> <class java.lang.Character: s>
+..=> public int java.lang.String.compareTo(java.lang.String)
+..<= public int java.lang.String.compareTo(java.lang.String) -> <class java.lang.Integer: 11>
+..=> public boolean java.lang.String.equals(java.lang.Object)
+..<= public boolean java.lang.String.equals(java.lang.Object) -> <class java.lang.Boolean: false>
+..=> public int java.lang.String.indexOf(int)
+..<= public int java.lang.String.indexOf(int) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(int,int)
+..<= public int java.lang.String.indexOf(int,int) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(java.lang.String)
+..<= public int java.lang.String.indexOf(java.lang.String) -> <class java.lang.Integer: -1>
+..=> public int java.lang.String.indexOf(java.lang.String,int)
+..<= public int java.lang.String.indexOf(java.lang.String,int) -> <class java.lang.Integer: -1>
+..=> public boolean java.lang.String.isEmpty()
+..<= public boolean java.lang.String.isEmpty() -> <class java.lang.Boolean: false>
+..=> public int java.lang.String.length()
+..<= public int java.lang.String.length() -> <class java.lang.Integer: 17>
+..=> public synchronized java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)
+..<= public synchronized java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String) -> <class java.lang.StringBuffer: some large string bufferhello>
+..=> public synchronized int java.lang.StringBuffer.length()
+..<= public synchronized int java.lang.StringBuffer.length() -> <class java.lang.Integer: 29>
+..=> public synchronized java.lang.String java.lang.StringBuffer.toString()
+..<= public synchronized java.lang.String java.lang.StringBuffer.toString() -> <class java.lang.String: some large string bufferhello>
+..=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
+..<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: some large string builderhello>
+..=> public int java.lang.StringBuilder.length()
+..<= public int java.lang.StringBuilder.length() -> <class java.lang.Integer: 30>
+..=> public java.lang.String java.lang.StringBuilder.toString()
+..<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: some large string builderhello>
+..=> public static java.lang.Integer java.lang.Integer.valueOf(int)
+..<= public static java.lang.Integer java.lang.Integer.valueOf(int) -> <class java.lang.Integer: 0>
+..=> public static boolean java.lang.Thread.interrupted()
+..<= public static boolean java.lang.Thread.interrupted() -> <class java.lang.Boolean: false>
+.<= static void art.Test988Intrinsics.test() -> <null: null>
+.=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+.=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+.<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+<= static void art.Test988$IntrinsicsTest.doTest() -> <null: null>
+=> public static java.lang.Thread java.lang.Thread.currentThread()
+<= public static java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
+=> public static void art.Trace.disableTracing(java.lang.Thread)
diff --git a/test/988-method-trace/expected_jack.diff b/test/988-method-trace/expected_jack.diff
new file mode 100644
index 0000000000..11364a0539
--- /dev/null
+++ b/test/988-method-trace/expected_jack.diff
@@ -0,0 +1,10 @@
+450,453c450,453
+< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+< .=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+< .<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
+---
+> .=> public static void java.lang.System.arraycopy(int[],int,int[],int,int)
+> .<= public static void java.lang.System.arraycopy(int[],int,int[],int,int) -> <null: null>
+> .=> public static void java.lang.System.arraycopy(char[],int,char[],int,int)
+> .<= public static void java.lang.System.arraycopy(char[],int,char[],int,int) -> <null: null>
diff --git a/test/988-method-trace/gen_srcs.py b/test/988-method-trace/gen_srcs.py
new file mode 100755
index 0000000000..c1ce35c278
--- /dev/null
+++ b/test/988-method-trace/gen_srcs.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python3
+# Copyright (C) 2017 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.
+
+#
+# Generates the src/art/Test988Intrinsics.java file.
+# Re-run this every time art/compiler/intrinics_list.h is modified.
+#
+# $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+#
+
+import argparse
+import os
+import re
+import collections
+import sys
+
+from string import Template
+
+# Relative path to art/compiler/intrinsics_list.h
+INTRINSICS_LIST_H = os.path.dirname(os.path.realpath(__file__)) + "/../../compiler/intrinsics_list.h"
+
+# Macro parameter index to V(). Negative means from the end.
+IDX_STATIC_OR_VIRTUAL = 1
+IDX_SIGNATURE = -1
+IDX_METHOD_NAME = -2
+IDX_CLASS_NAME = -3
+
+# Exclude all hidden API.
+KLASS_BLACK_LIST = ['sun.misc.Unsafe', 'libcore.io.Memory', 'java.lang.StringFactory']
+METHOD_BLACK_LIST = [('java.lang.ref.Reference', 'getReferent'),
+ ('java.lang.String', 'getCharsNoCheck'),
+ ('java.lang.System', 'arraycopy')] # arraycopy has a manual test.
+
+# When testing a virtual function, it needs to operate on an instance.
+# These instances will be created with the following values,
+# otherwise a default 'new T()' is used.
+KLASS_INSTANCE_INITIALIZERS = {
+ 'java.lang.String' : '"some large string"',
+ 'java.lang.StringBuffer' : 'new java.lang.StringBuffer("some large string buffer")',
+ 'java.lang.StringBuilder' : 'new java.lang.StringBuilder("some large string builder")',
+ 'java.lang.ref.Reference' : 'new java.lang.ref.WeakReference(new Object())'
+};
+
+OUTPUT_TPL = Template("""
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY.
+//
+// $$> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+//
+// RUN ABOVE COMMAND TO REGENERATE THIS FILE.
+
+package art;
+
+class Test988Intrinsics {
+ // Pre-initialize *all* instance variables used so that their constructors are not in the trace.
+$static_fields
+
+ static void initialize() {
+ // Ensure all static variables are initialized.
+ // In addition, pre-load classes here so that we don't see diverging class loading traces.
+$initialize_classes
+ }
+
+ static void test() {
+ // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+$test_body
+ }
+}
+""")
+
+JNI_TYPES = {
+ 'Z' : 'boolean',
+ 'B' : 'byte',
+ 'C' : 'char',
+ 'S' : 'short',
+ 'I' : 'int',
+ 'J' : 'long',
+ 'F' : 'float',
+ 'D' : 'double',
+ 'L' : 'object'
+};
+
+debug_printing_enabled = False
+
+def debug_print(x):
+ if debug_printing_enabled:
+ print(x, file=sys.stderr)
+
+# Parse JNI sig into a list, e.g. "II" -> ['I', 'I'], '[[IJ' -> ['[[I', 'J'], etc.
+def sig_to_parameter_type_list(sig):
+ sig = re.sub(r'[(](.*)[)].*', r'\1', sig)
+
+ lst = []
+ obj = ""
+ is_obj = False
+ is_array = False
+ for x in sig:
+ if is_obj:
+ obj = obj + x
+ if x == ";":
+ is_obj = False
+ lst.append(obj)
+ obj = ""
+ elif is_array:
+ obj = obj + x
+ if x != "[":
+ is_array = False
+ lst.append(obj)
+ obj = ""
+ else:
+ if x == "[":
+ obj = "["
+ is_array = True
+ elif x == "L":
+ obj = "L"
+ is_obj = True
+ else:
+ lst.append(x)
+
+ return lst
+
+# Convert a single JNI descriptor into a pretty java name, e.g. "[I" -> "int[]", etc.
+def javafy_name(kls_name):
+ if kls_name.startswith("L"):
+ kls_name = kls_name.lstrip("L").rstrip(";")
+ return kls_name.replace("/", ".")
+ elif kls_name.startswith("["):
+ array_count = kls_name.count("[")
+ non_array = javafy_name(kls_name.lstrip("["))
+ return non_array + ("[]" * array_count)
+
+ return JNI_TYPES.get(kls_name, kls_name)
+
+def extract_staticness(static_or_virtual):
+ if static_or_virtual == "kStatic":
+ return 'static'
+ return 'virtual' # kVirtual, kDirect
+
+class MethodInfo:
+ def __init__(self, staticness, pretty_params, method, kls):
+ # 'virtual' or 'static'
+ self.staticness = staticness
+ # list of e.g. ['int', 'double', 'java.lang.String'] etc
+ self.parameters = pretty_params
+ # e.g. 'toString'
+ self.method_name = method
+ # e.g. 'java.lang.String'
+ self.klass = kls
+
+ def __str__(self):
+ return "MethodInfo " + str(self.__dict__)
+
+ def dummy_parameters(self):
+ dummy_values = {
+ 'boolean' : 'false',
+ 'byte' : '(byte)0',
+ 'char' : "'x'",
+ 'short' : '(short)0',
+ 'int' : '0',
+ 'long' : '0L',
+ 'float' : '0.0f',
+ 'double' : '0.0'
+ }
+
+ def object_dummy(name):
+ if name == "java.lang.String":
+ return '"hello"'
+ else:
+ return "(%s)null" %(name)
+ return [ dummy_values.get(param, object_dummy(param)) for param in self.parameters ]
+
+ def dummy_instance_value(self):
+ return KLASS_INSTANCE_INITIALIZERS.get(self.klass, 'new %s()' %(self.klass))
+
+ def is_blacklisted(self):
+ for blk in KLASS_BLACK_LIST:
+ if self.klass.startswith(blk):
+ return True
+
+ return (self.klass, self.method_name) in METHOD_BLACK_LIST
+
+# parse the V(...) \ list of items into a MethodInfo
+def parse_method_info(items):
+ def get_item(idx):
+ return items[idx].strip().strip("\"")
+
+ staticness = get_item(IDX_STATIC_OR_VIRTUAL)
+ sig = get_item(IDX_SIGNATURE)
+ method = get_item(IDX_METHOD_NAME)
+ kls = get_item(IDX_CLASS_NAME)
+
+ debug_print ((sig, method, kls))
+
+ staticness = extract_staticness(staticness)
+ kls = javafy_name(kls)
+ param_types = sig_to_parameter_type_list(sig)
+ pretty_params = param_types
+ pretty_params = [javafy_name(i) for i in param_types]
+
+ return MethodInfo(staticness, pretty_params, method, kls)
+
+# parse a line containing ' V(...)' into a MethodInfo
+def parse_line(line):
+ line = line.strip()
+ if not line.startswith("V("):
+ return None
+
+ line = re.sub(r'V[(](.*)[)]', r'\1', line)
+ debug_print(line)
+
+ items = line.split(",")
+
+ method_info = parse_method_info(items)
+ return method_info
+
+# Generate all the MethodInfo that we parse from intrinsics_list.h
+def parse_all_method_infos():
+ with open(INTRINSICS_LIST_H) as f:
+ for line in f:
+ s = parse_line(line)
+ if s is not None:
+ yield s
+
+# Format a receiver name. For statics, it's the class name, for receivers, it's an instance variable
+def format_receiver_name(method_info):
+ receiver = method_info.klass
+ if method_info.staticness == 'virtual':
+ receiver = "instance_" + method_info.klass.replace(".", "_")
+ return receiver
+
+# Format a dummy call with dummy method parameters to the requested method.
+def format_call_to(method_info):
+ dummy_args = ", ".join(method_info.dummy_parameters())
+ receiver = format_receiver_name(method_info)
+
+ return ("%s.%s(%s);" %(receiver, method_info.method_name, dummy_args))
+
+# Format a static variable with an instance that could be used as the receiver
+# (or None for non-static methods).
+def format_instance_variable(method_info):
+ if method_info.staticness == 'static':
+ return None
+ return "static %s %s = %s;" %(method_info.klass, format_receiver_name(method_info), method_info.dummy_instance_value())
+
+def format_initialize_klass(method_info):
+ return "%s.class.toString();" %(method_info.klass)
+
+def indent_list(lst, indent):
+ return [' ' * indent + i for i in lst]
+
+def main():
+ global debug_printing_enabled
+ parser = argparse.ArgumentParser(description='Generate art/test/988-method-trace/src/art/Test988Intrinsics.java')
+ parser.add_argument('-d', '--debug', action='store_true', dest='debug', help='Print extra debugging information to stderr.')
+ parser.add_argument('output_file', nargs='?', metavar='<output-file>', default=sys.stdout, type=argparse.FileType('w'), help='Destination file to write to (default: stdout).')
+ args = parser.parse_args()
+
+ debug_printing_enabled = args.debug
+
+ #####
+
+ call_str_list = []
+ instance_variable_dict = collections.OrderedDict()
+ initialize_klass_dict = collections.OrderedDict()
+ for i in parse_all_method_infos():
+ debug_print(i)
+ if i.is_blacklisted():
+ debug_print("Blacklisted: " + str(i))
+ continue
+
+ call_str = format_call_to(i)
+ debug_print(call_str)
+
+ call_str_list.append(call_str)
+
+ instance_variable = format_instance_variable(i)
+ if instance_variable is not None:
+ debug_print(instance_variable)
+ instance_variable_dict[i.klass] = instance_variable
+
+ initialize_klass_dict[i.klass] = format_initialize_klass(i)
+
+ static_fields = indent_list([ value for (key, value) in instance_variable_dict.items() ], 2)
+ test_body = indent_list(call_str_list, 4)
+ initialize_classes = indent_list([ value for (key, value) in initialize_klass_dict.items() ], 4)
+
+ print(OUTPUT_TPL.substitute(static_fields="\n".join(static_fields),
+ test_body="\n".join(test_body),
+ initialize_classes="\n".join(initialize_classes)).
+ strip("\n"), \
+ file=args.output_file)
+
+if __name__ == '__main__':
+ main()
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 6a45c0eaa2..d7eda524c4 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -31,6 +31,7 @@ public class Test988 {
// Methods with non-deterministic output that should not be printed.
static Set<Method> NON_DETERMINISTIC_OUTPUT_METHODS = new HashSet<>();
+ static Set<Method> NON_DETERMINISTIC_OUTPUT_TYPE_METHODS = new HashSet<>();
static {
try {
@@ -39,6 +40,7 @@ public class Test988 {
} catch (Exception e) {}
try {
NON_DETERMINISTIC_OUTPUT_METHODS.add(Thread.class.getDeclaredMethod("currentThread"));
+ NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.add(Thread.class.getDeclaredMethod("currentThread"));
} catch (Exception e) {}
}
@@ -55,7 +57,7 @@ public class Test988 {
}
@Override
public void Print() {
- System.out.println(whitespace(cnt) + "=> " + m);
+ System.out.println(whitespace(cnt) + "=> " + methodToString(m));
}
}
@@ -66,7 +68,16 @@ public class Test988 {
return arrayToString(val);
} else if (val instanceof Throwable) {
StringWriter w = new StringWriter();
- ((Throwable) val).printStackTrace(new PrintWriter(w));
+ Throwable thr = ((Throwable) val);
+ w.write(thr.getClass().getName() + ": " + thr.getMessage() + "\n");
+ for (StackTraceElement e : thr.getStackTrace()) {
+ if (e.getClassName().startsWith("art.")) {
+ w.write("\t" + e + "\n");
+ } else {
+ w.write("\t<additional hidden frames>\n");
+ break;
+ }
+ }
return w.toString();
} else {
return val.toString();
@@ -113,6 +124,13 @@ public class Test988 {
}
}
+ static String methodToString(Object m) {
+ // Make the output more similar between ART and RI,
+ // by removing the 'native' specifier from methods.
+ String methodStr = m.toString();
+ return methodStr.replaceFirst(" native", "");
+ }
+
static final class MethodReturn implements Printable {
private Object m;
private Object val;
@@ -134,8 +152,16 @@ public class Test988 {
if (val != null) {
klass = val.getClass();
}
+ String klass_print;
+ if (klass == null) {
+ klass_print = "null";
+ } else if (NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.contains(m)) {
+ klass_print = "<non-deterministic>";
+ } else {
+ klass_print = klass.toString();
+ }
System.out.println(
- whitespace(cnt) + "<= " + m + " -> <" + klass + ": " + print + ">");
+ whitespace(cnt) + "<= " + methodToString(m) + " -> <" + klass_print + ": " + print + ">");
}
}
@@ -148,7 +174,7 @@ public class Test988 {
}
@Override
public void Print() {
- System.out.println(whitespace(cnt) + "<= " + m + " EXCEPTION");
+ System.out.println(whitespace(cnt) + "<= " + methodToString(m) + " EXCEPTION");
}
}
@@ -236,15 +262,26 @@ public class Test988 {
}
}
+ static final int METHOD_TRACING_IGNORE_DEPTH = 2;
+ static boolean sMethodTracingIgnore = false;
+
public static void notifyMethodEntry(Object m) {
// Called by native code when a method is entered. This method is ignored by the native
// entry and exit hooks.
- results.add(new MethodEntry(m, cnt));
cnt++;
+ if ((cnt - 1) > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
+ return;
+ }
+ results.add(new MethodEntry(m, cnt - 1));
}
public static void notifyMethodExit(Object m, boolean exception, Object result) {
cnt--;
+
+ if (cnt > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
+ return;
+ }
+
if (exception) {
results.add(new MethodThrownThrough(m, cnt));
} else {
@@ -266,6 +303,10 @@ public class Test988 {
doFibTest(5, new RecurOp());
doFibTest(-19, new IterOp());
doFibTest(-19, new RecurOp());
+
+ sMethodTracingIgnore = true;
+ IntrinsicsTest.doTest();
+ sMethodTracingIgnore = false;
// Turn off method tracing so we don't have to deal with print internals.
Trace.disableTracing(Thread.currentThread());
printResults();
@@ -284,6 +325,7 @@ public class Test988 {
RecurOp.class.toString();
IterOp.class.toString();
StringBuilder.class.toString();
+ IntrinsicsTest.initialize(); // ensure <clinit> is executed prior to tracing.
}
public static void printResults() {
@@ -300,4 +342,30 @@ public class Test988 {
results.add(new FibThrow("fibonacci(%d) -> %s\n", x, t));
}
}
+
+ static class IntrinsicsTest {
+ static int[] sSourceArray = { 0, 1, 2, 3, 4, 5 };
+ static int[] sDestArray = { 5, 6, 7, 8, 9, 10 };
+
+ static char[] sSourceArrayChar = { '0', '1', '2', '3', '4', '5' };
+ static char[] sDestArrayChar = { '5', '6', '7', '8', '9', 'a' };
+
+ static void initialize() {
+ Test988Intrinsics.initialize();
+
+ // Pre-load all classes used in #doTest manual intrinsics.
+ java.lang.System.class.toString();
+ }
+ static void doTest() {
+ // Ensure that the ART intrinsics in intrinsics_list.h are also being traced,
+ // since in non-tracing operation they are effectively inlined by the optimizing compiler.
+
+ // Auto-generated test file that uses null/0s as default parameters.
+ Test988Intrinsics.test();
+
+ // Manual list here for functions that require special non-null/non-zero parameters:
+ System.arraycopy(sSourceArray, 0, sDestArray, 0, 1);
+ System.arraycopy(sSourceArrayChar, 0, sDestArrayChar, 0, 1);
+ }
+ }
}
diff --git a/test/988-method-trace/src/art/Test988Intrinsics.java b/test/988-method-trace/src/art/Test988Intrinsics.java
new file mode 100644
index 0000000000..099fbf2ce8
--- /dev/null
+++ b/test/988-method-trace/src/art/Test988Intrinsics.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// AUTO-GENENERATED by gen_srcs.py: DO NOT EDIT HERE DIRECTLY.
+//
+// $> python3.4 gen_srcs.py > src/art/Test988Intrinsics.java
+//
+// RUN ABOVE COMMAND TO REGENERATE THIS FILE.
+
+package art;
+
+class Test988Intrinsics {
+ // Pre-initialize *all* instance variables used so that their constructors are not in the trace.
+ static java.lang.String instance_java_lang_String = "some large string";
+ static java.lang.StringBuffer instance_java_lang_StringBuffer = new java.lang.StringBuffer("some large string buffer");
+ static java.lang.StringBuilder instance_java_lang_StringBuilder = new java.lang.StringBuilder("some large string builder");
+
+ static void initialize() {
+ // Ensure all static variables are initialized.
+ // In addition, pre-load classes here so that we don't see diverging class loading traces.
+ java.lang.Double.class.toString();
+ java.lang.Float.class.toString();
+ java.lang.Integer.class.toString();
+ java.lang.Long.class.toString();
+ java.lang.Short.class.toString();
+ java.lang.Math.class.toString();
+ java.lang.Thread.class.toString();
+ java.lang.String.class.toString();
+ java.lang.StringBuffer.class.toString();
+ java.lang.StringBuilder.class.toString();
+ }
+
+ static void test() {
+ // Call each intrinsic from art/compiler/intrinsics_list.h to make sure they are traced.
+ java.lang.Double.doubleToRawLongBits(0.0);
+ java.lang.Double.doubleToLongBits(0.0);
+ java.lang.Double.isInfinite(0.0);
+ java.lang.Double.isNaN(0.0);
+ java.lang.Double.longBitsToDouble(0L);
+ java.lang.Float.floatToRawIntBits(0.0f);
+ java.lang.Float.floatToIntBits(0.0f);
+ java.lang.Float.isInfinite(0.0f);
+ java.lang.Float.isNaN(0.0f);
+ java.lang.Float.intBitsToFloat(0);
+ java.lang.Integer.reverse(0);
+ java.lang.Integer.reverseBytes(0);
+ java.lang.Integer.bitCount(0);
+ java.lang.Integer.compare(0, 0);
+ java.lang.Integer.highestOneBit(0);
+ java.lang.Integer.lowestOneBit(0);
+ java.lang.Integer.numberOfLeadingZeros(0);
+ java.lang.Integer.numberOfTrailingZeros(0);
+ java.lang.Integer.rotateRight(0, 0);
+ java.lang.Integer.rotateLeft(0, 0);
+ java.lang.Integer.signum(0);
+ java.lang.Long.reverse(0L);
+ java.lang.Long.reverseBytes(0L);
+ java.lang.Long.bitCount(0L);
+ java.lang.Long.compare(0L, 0L);
+ java.lang.Long.highestOneBit(0L);
+ java.lang.Long.lowestOneBit(0L);
+ java.lang.Long.numberOfLeadingZeros(0L);
+ java.lang.Long.numberOfTrailingZeros(0L);
+ java.lang.Long.rotateRight(0L, 0);
+ java.lang.Long.rotateLeft(0L, 0);
+ java.lang.Long.signum(0L);
+ java.lang.Short.reverseBytes((short)0);
+ java.lang.Math.abs(0.0);
+ java.lang.Math.abs(0.0f);
+ java.lang.Math.abs(0L);
+ java.lang.Math.abs(0);
+ java.lang.Math.min(0.0, 0.0);
+ java.lang.Math.min(0.0f, 0.0f);
+ java.lang.Math.min(0L, 0L);
+ java.lang.Math.min(0, 0);
+ java.lang.Math.max(0.0, 0.0);
+ java.lang.Math.max(0.0f, 0.0f);
+ java.lang.Math.max(0L, 0L);
+ java.lang.Math.max(0, 0);
+ java.lang.Math.cos(0.0);
+ java.lang.Math.sin(0.0);
+ java.lang.Math.acos(0.0);
+ java.lang.Math.asin(0.0);
+ java.lang.Math.atan(0.0);
+ java.lang.Math.atan2(0.0, 0.0);
+ java.lang.Math.cbrt(0.0);
+ java.lang.Math.cosh(0.0);
+ java.lang.Math.exp(0.0);
+ java.lang.Math.expm1(0.0);
+ java.lang.Math.hypot(0.0, 0.0);
+ java.lang.Math.log(0.0);
+ java.lang.Math.log10(0.0);
+ java.lang.Math.nextAfter(0.0, 0.0);
+ java.lang.Math.sinh(0.0);
+ java.lang.Math.tan(0.0);
+ java.lang.Math.tanh(0.0);
+ java.lang.Math.sqrt(0.0);
+ java.lang.Math.ceil(0.0);
+ java.lang.Math.floor(0.0);
+ java.lang.Math.rint(0.0);
+ java.lang.Math.round(0.0);
+ java.lang.Math.round(0.0f);
+ java.lang.Thread.currentThread();
+ instance_java_lang_String.charAt(0);
+ instance_java_lang_String.compareTo("hello");
+ instance_java_lang_String.equals((java.lang.Object)null);
+ instance_java_lang_String.indexOf(0);
+ instance_java_lang_String.indexOf(0, 0);
+ instance_java_lang_String.indexOf("hello");
+ instance_java_lang_String.indexOf("hello", 0);
+ instance_java_lang_String.isEmpty();
+ instance_java_lang_String.length();
+ instance_java_lang_StringBuffer.append("hello");
+ instance_java_lang_StringBuffer.length();
+ instance_java_lang_StringBuffer.toString();
+ instance_java_lang_StringBuilder.append("hello");
+ instance_java_lang_StringBuilder.length();
+ instance_java_lang_StringBuilder.toString();
+ java.lang.Integer.valueOf(0);
+ java.lang.Thread.interrupted();
+ }
+}
diff --git a/test/988-method-trace/src/art/Trace.java b/test/988-method-trace/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/988-method-trace/src/art/Trace.java
+++ b/test/988-method-trace/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/989-method-trace-throw/src/art/Trace.java
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/990-field-trace/src/art/Trace.java b/test/990-field-trace/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/990-field-trace/src/art/Trace.java
+++ b/test/990-field-trace/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/991-field-trace-2/src/art/Trace.java b/test/991-field-trace-2/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/991-field-trace-2/src/art/Trace.java
+++ b/test/991-field-trace-2/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/992-source-data/expected.txt b/test/992-source-data/expected.txt
index 480d8a4fe7..4db8df0ada 100644
--- a/test/992-source-data/expected.txt
+++ b/test/992-source-data/expected.txt
@@ -1,6 +1,6 @@
class art.Test992 is defined in file "Test992.java"
class art.Test992$Target1 is defined in file "Test992.java"
-class art.Test2 is defined in file "Test2.java"
+class art.Target2 is defined in file "Target2.java"
int does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION
class java.lang.Integer is defined in file "Integer.java"
class java.lang.Object is defined in file "Object.java"
diff --git a/test/992-source-data/src/art/Test2.java b/test/992-source-data/src/art/Target2.java
index dbb1089c5e..7c29d88871 100644
--- a/test/992-source-data/src/art/Test2.java
+++ b/test/992-source-data/src/art/Target2.java
@@ -16,4 +16,4 @@
package art;
-public class Test2 {}
+public class Target2 {}
diff --git a/test/992-source-data/src/art/Test992.java b/test/992-source-data/src/art/Test992.java
index db6ea73856..d9ab112726 100644
--- a/test/992-source-data/src/art/Test992.java
+++ b/test/992-source-data/src/art/Test992.java
@@ -25,7 +25,7 @@ public class Test992 {
public static void run() {
doTest(Test992.class);
doTest(Target1.class);
- doTest(Test2.class);
+ doTest(Target2.class);
doTest(Integer.TYPE);
doTest(Integer.class);
doTest(Object.class);
diff --git a/test/993-breakpoints/breakpoints.cc b/test/993-breakpoints/breakpoints.cc
new file mode 100644
index 0000000000..129207098d
--- /dev/null
+++ b/test/993-breakpoints/breakpoints.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test993Breakpoints {
+
+extern "C" JNIEXPORT
+jobject JNICALL Java_art_Test993_constructNative(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ return env->NewObject(clazz, method);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test993_invokeNative(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz,
+ jobject thizz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (thizz == nullptr) {
+ env->CallStaticVoidMethod(clazz, method);
+ } else {
+ env->CallVoidMethod(thizz, method);
+ }
+}
+
+} // namespace Test993Breakpoints
+} // namespace art
+
diff --git a/test/993-breakpoints/expected.txt b/test/993-breakpoints/expected.txt
new file mode 100644
index 0000000000..962154734b
--- /dev/null
+++ b/test/993-breakpoints/expected.txt
@@ -0,0 +1,613 @@
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints/info.txt b/test/993-breakpoints/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/993-breakpoints/info.txt
@@ -0,0 +1,7 @@
+Test basic JVMTI breakpoint functionality.
+
+This test places a breakpoint on the first instruction of a number of functions
+that are entered in every way possible for the given class of method.
+
+It also tests that breakpoints don't interfere with each other by having
+multiple breakpoints be set at once.
diff --git a/test/993-breakpoints/run b/test/993-breakpoints/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/993-breakpoints/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/993-breakpoints/src/Main.java b/test/993-breakpoints/src/Main.java
new file mode 100644
index 0000000000..b11f6f8174
--- /dev/null
+++ b/test/993-breakpoints/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test993.run();
+ }
+}
diff --git a/test/993-breakpoints/src/art/Breakpoint.java b/test/993-breakpoints/src/art/Breakpoint.java
new file mode 100644
index 0000000000..2a370ebd40
--- /dev/null
+++ b/test/993-breakpoints/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/993-breakpoints/src/art/Test993.java b/test/993-breakpoints/src/art/Test993.java
new file mode 100644
index 0000000000..781ebffc0f
--- /dev/null
+++ b/test/993-breakpoints/src/art/Test993.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Supplier;
+
+public class Test993 {
+
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+
+ // A function we can use as a start breakpoint.
+ public static void breakpoint() {
+ return;
+ }
+
+ private static void privateBreakpoint() {
+ return;
+ }
+
+ // An interface with a default method we can break on.
+ static interface Breakable {
+ public static void iBreakpoint() {
+ return;
+ }
+
+ public default void breakit() {
+ return;
+ }
+ }
+
+ // A class that has a default method we breakpoint on.
+ public static class TestClass1 implements Breakable {
+ public TestClass1() {
+ super();
+ }
+ public String toString() { return "TestClass1"; }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super.
+ public static class TestClass1ext extends TestClass1 {
+ public TestClass1ext() {
+ super();
+ }
+ public String toString() { return "TestClass1Ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+
+ // A class that overrides a default method that we can breakpoint on.
+ public static class TestClass2 implements Breakable {
+ public String toString() { return "TestClass2"; }
+ public void breakit() {
+ return;
+ }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super.
+ public static class TestClass2ext extends TestClass2 {
+ public String toString() { return "TestClass2ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+ // A class that overrides a default method and calls it directly with interface invoke-super
+ public static class TestClass3 implements Breakable {
+ public String toString() { return "TestClass3"; }
+ public void breakit() {
+ Breakable.super.breakit();
+ }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super to a class
+ // that uses interface-invoke-super.
+ public static class TestClass3ext extends TestClass3 {
+ public String toString() { return "TestClass3ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+ public static class TestClass4 {
+ public String toString() { return "TestClass4"; }
+ public void callPrivateMethod() {
+ privateMethod();
+ }
+ private void privateMethod() {
+ return;
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\t\t\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ }
+
+ public static interface ThrowRunnable extends Runnable {
+ public default void run() {
+ try {
+ runThrow();
+ } catch (Exception e) {
+ throw new Error("Caught error while running " + this, e);
+ }
+ }
+ public void runThrow() throws Exception;
+ }
+
+ public static class InvokeDirect implements Runnable {
+ String msg;
+ Runnable r;
+ public InvokeDirect(String msg, Runnable r) {
+ this.msg = msg;
+ this.r = r;
+ }
+ @Override
+ public void run() {
+ System.out.println("\t\tInvoking \"" + msg + "\"");
+ r.run();
+ }
+ }
+
+ public static class InvokeReflect implements ThrowRunnable {
+ Method m;
+ Object this_arg;
+ public InvokeReflect(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void runThrow() throws Exception {
+ System.out.println("\t\tReflective invoking: " + m + " args: [this: " + this_arg + "]");
+ m.invoke(this_arg);
+ }
+ }
+
+ public static class InvokeNative implements Runnable {
+ Method m;
+ Object this_arg;
+ public InvokeNative(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative invoking: " + m + " args: [this: " + this_arg + "]");
+ invokeNative(m, m.getDeclaringClass(), this_arg);
+ }
+ }
+
+ public static native void invokeNative(Method m, Class<?> clazz, Object thizz);
+
+ public static class ConstructDirect implements Runnable {
+ String msg;
+ Supplier<Object> s;
+ public ConstructDirect(String msg, Supplier<Object> s) {
+ this.msg = msg;
+ this.s = s;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tConstructing: " + msg);
+ System.out.println("\t\t\tCreated: " + s.get());
+ }
+ }
+
+ public static class ConstructReflect implements ThrowRunnable {
+ Constructor<?> m;
+ public ConstructReflect(Constructor<?> m) {
+ this.m = m;
+ }
+
+ @Override
+ public void runThrow() throws Exception {
+ System.out.println("\t\tReflective constructor: " + m);
+ System.out.println("\t\t\tCreated: " + m.newInstance());
+ }
+ }
+
+ public static class ConstructNative implements Runnable {
+ Constructor<?> m;
+ Class type;
+ public ConstructNative(Constructor<?> m) {
+ this.m = m;
+ this.type = m.getDeclaringClass();
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative constructor: " + m + ", type: " + type);
+ System.out.println("\t\t\tCreated: " + constructNative(m, type));
+ }
+ }
+
+ public static native Object constructNative(Constructor m, Class<?> clazz);
+
+ private static <T> List<List<T>> combinations(List<T> items, int len) {
+ if (len > items.size()) {
+ throw new Error("Bad length" + len + " " + items);
+ }
+ if (len == 1) {
+ List<List<T>> out = new ArrayList<>();
+ for (T t : items) {
+ out.add(Arrays.asList(t));
+ }
+ return out;
+ }
+ List<List<T>> out = new ArrayList<>();
+ for (int rem = 0; rem <= items.size() - len; rem++) {
+ for (List<T> others : combinations(items.subList(rem + 1, items.size()), len - 1)) {
+ List<T> newone = new ArrayList<>();
+ newone.add(items.get(rem));
+ newone.addAll(others);
+ out.add(newone);
+ }
+ }
+ return out;
+ }
+
+ private static <T> List<List<T>> allCombinations(List<T> items) {
+ List<List<T>> out = new ArrayList<List<T>>();
+ out.add(new ArrayList<>());
+ for (int i = 0; i < items.size(); i++) {
+ out.addAll(combinations(items, i + 1));
+ }
+ return out;
+ }
+
+ private static Breakpoint.Manager.BP BP(Executable m) {
+ return new Breakpoint.Manager.BP(m);
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test993.class,
+ Test993.class.getDeclaredMethod("notifyBreakpointReached",
+ Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ runMethodTests();
+ runConstructorTests();
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+
+ public static void runConstructorTests() throws Exception {
+ // The constructors we will be breaking on.
+ Constructor<?> tc1_construct = TestClass1.class.getConstructor();
+ Constructor<?> tc1ext_construct = TestClass1ext.class.getConstructor();
+
+ Runnable[] tc1_constructors = new Runnable[] {
+ new ConstructNative(tc1_construct),
+ new ConstructReflect(tc1_construct),
+ new ConstructDirect("new TestClass1()", TestClass1::new),
+ };
+ Breakpoint.Manager.BP[] tc1_bps = new Breakpoint.Manager.BP[] {
+ BP(tc1_construct),
+ };
+ runTestGroups("TestClass1 constructor", tc1_constructors, tc1_bps);
+
+ Runnable[] tc1ext_constructors = new Runnable[] {
+ new ConstructNative(tc1ext_construct),
+ new ConstructReflect(tc1ext_construct),
+ new ConstructDirect("new TestClass1ext()", TestClass1ext::new),
+ };
+ Breakpoint.Manager.BP[] tc1ext_bps = new Breakpoint.Manager.BP[] {
+ BP(tc1_construct), BP(tc1ext_construct),
+ };
+ runTestGroups("TestClass1ext constructor", tc1ext_constructors, tc1ext_bps);
+ }
+
+ public static void runMethodTests() throws Exception {
+ // The methods we will be breaking on.
+ Method breakpoint_method = Test993.class.getDeclaredMethod("breakpoint");
+ Method private_breakpoint_method = Test993.class.getDeclaredMethod("privateBreakpoint");
+ Method i_breakpoint_method = Breakable.class.getDeclaredMethod("iBreakpoint");
+ Method breakit_method = Breakable.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc1ext = TestClass1ext.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc2 = TestClass2.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc2ext = TestClass2ext.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc3 = TestClass3.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc3ext = TestClass3ext.class.getDeclaredMethod("breakit");
+ Method private_method = TestClass4.class.getDeclaredMethod("privateMethod");
+
+ // Static class function
+ Runnable[] static_invokes = new Runnable[] {
+ new InvokeNative(breakpoint_method, null),
+
+ new InvokeReflect(breakpoint_method, null),
+
+ new InvokeDirect("Test993::breakpoint", Test993::breakpoint),
+ };
+ Breakpoint.Manager.BP[] static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakpoint_method)
+ };
+ runTestGroups("static invoke", static_invokes, static_breakpoints);
+
+ // Static private class function
+ Runnable[] private_static_invokes = new Runnable[] {
+ new InvokeNative(private_breakpoint_method, null),
+
+ new InvokeDirect("Test993::privateBreakpoint", Test993::privateBreakpoint),
+ };
+ Breakpoint.Manager.BP[] private_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(private_breakpoint_method)
+ };
+ runTestGroups("private static invoke", private_static_invokes, private_static_breakpoints);
+
+ // Static interface function.
+ Runnable[] i_static_invokes = new Runnable[] {
+ new InvokeNative(i_breakpoint_method, null),
+
+ new InvokeReflect(i_breakpoint_method, null),
+
+ new InvokeDirect("Breakable::iBreakpoint", Breakable::iBreakpoint),
+ };
+ Breakpoint.Manager.BP[] i_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(i_breakpoint_method)
+ };
+ runTestGroups("interface static invoke", i_static_invokes, i_static_breakpoints);
+
+ // Call default method through a class.
+ Runnable[] tc1_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass1()),
+
+ new InvokeReflect(breakit_method, new TestClass1()),
+
+ new InvokeDirect("((Breakable)new TestClass1()).breakit()",
+ () -> ((Breakable)new TestClass1()).breakit()),
+ new InvokeDirect("new TestClass1().breakit()",
+ () -> new TestClass1().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc1_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method)
+ };
+ runTestGroups("TestClass1 invokes", tc1_invokes, tc1_breakpoints);
+
+ // Call default method through an override and normal invoke-super
+ Runnable[] tc1ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass1ext()),
+ new InvokeNative(breakit_method_tc1ext, new TestClass1ext()),
+
+ new InvokeReflect(breakit_method, new TestClass1ext()),
+ new InvokeReflect(breakit_method_tc1ext, new TestClass1ext()),
+
+ new InvokeDirect("((Breakable)new TestClass1ext()).breakit()",
+ () -> ((Breakable)new TestClass1ext()).breakit()),
+ new InvokeDirect("((TestClass1)new TestClass1ext()).breakit()",
+ () -> ((TestClass1)new TestClass1ext()).breakit()),
+ new InvokeDirect("new TestClass1ext().breakit()",
+ () -> new TestClass1ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc1ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc1ext)
+ };
+ runTestGroups("TestClass1ext invokes", tc1ext_invokes, tc1ext_breakpoints);
+
+ // Override default/interface method.
+ Runnable[] tc2_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass2()),
+ new InvokeNative(breakit_method_tc2, new TestClass2()),
+
+ new InvokeReflect(breakit_method, new TestClass2()),
+ new InvokeReflect(breakit_method_tc2, new TestClass2()),
+
+ new InvokeDirect("((Breakable)new TestClass2()).breakit()",
+ () -> ((Breakable)new TestClass2()).breakit()),
+ new InvokeDirect("new TestClass2().breakit()",
+ () -> new TestClass2().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc2_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc2)
+ };
+ runTestGroups("TestClass2 invokes", tc2_invokes, tc2_breakpoints);
+
+ // Call overridden method using invoke-super
+ Runnable[] tc2ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass2ext()),
+ new InvokeNative(breakit_method_tc2, new TestClass2ext()),
+ new InvokeNative(breakit_method_tc2ext, new TestClass2ext()),
+
+ new InvokeReflect(breakit_method, new TestClass2ext()),
+ new InvokeReflect(breakit_method_tc2, new TestClass2ext()),
+ new InvokeReflect(breakit_method_tc2ext, new TestClass2ext()),
+
+ new InvokeDirect("((Breakable)new TestClass2ext()).breakit()",
+ () -> ((Breakable)new TestClass2ext()).breakit()),
+ new InvokeDirect("((TestClass2)new TestClass2ext()).breakit()",
+ () -> ((TestClass2)new TestClass2ext()).breakit()),
+ new InvokeDirect("new TestClass2ext().breakit())",
+ () -> new TestClass2ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc2ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc2), BP(breakit_method_tc2ext)
+ };
+ runTestGroups("TestClass2ext invokes", tc2ext_invokes, tc2ext_breakpoints);
+
+ // Override default method and call it using interface-invoke-super
+ Runnable[] tc3_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass3()),
+ new InvokeNative(breakit_method_tc3, new TestClass3()),
+
+ new InvokeReflect(breakit_method, new TestClass3()),
+ new InvokeReflect(breakit_method_tc3, new TestClass3()),
+
+ new InvokeDirect("((Breakable)new TestClass3()).breakit()",
+ () -> ((Breakable)new TestClass3()).breakit()),
+ new InvokeDirect("new TestClass3().breakit())",
+ () -> new TestClass3().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc3_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc3)
+ };
+ runTestGroups("TestClass3 invokes", tc3_invokes, tc3_breakpoints);
+
+ // Call overridden method using invoke-super
+ Runnable[] tc3ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass3ext()),
+ new InvokeNative(breakit_method_tc3, new TestClass3ext()),
+ new InvokeNative(breakit_method_tc3ext, new TestClass3ext()),
+
+ new InvokeReflect(breakit_method, new TestClass3ext()),
+ new InvokeReflect(breakit_method_tc3, new TestClass3ext()),
+ new InvokeReflect(breakit_method_tc3ext, new TestClass3ext()),
+
+ new InvokeDirect("((Breakable)new TestClass3ext()).breakit()",
+ () -> ((Breakable)new TestClass3ext()).breakit()),
+ new InvokeDirect("((TestClass3)new TestClass3ext()).breakit()",
+ () -> ((TestClass3)new TestClass3ext()).breakit()),
+ new InvokeDirect("new TestClass3ext().breakit())",
+ () -> new TestClass3ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc3ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc3), BP(breakit_method_tc3ext)
+ };
+ runTestGroups("TestClass3ext invokes", tc3ext_invokes, tc3ext_breakpoints);
+
+ // private instance method.
+ Runnable[] private_instance_invokes = new Runnable[] {
+ new InvokeNative(private_method, new TestClass4()),
+
+ new InvokeDirect("new TestClass4().callPrivateMethod()",
+ () -> new TestClass4().callPrivateMethod()),
+ };
+ Breakpoint.Manager.BP[] private_instance_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(private_method)
+ };
+ runTestGroups(
+ "private instance invoke", private_instance_invokes, private_instance_breakpoints);
+ }
+
+ private static void runTestGroups(String name,
+ Runnable[] invokes,
+ Breakpoint.Manager.BP[] breakpoints) throws Exception {
+ System.out.println("Running " + name);
+ for (List<Breakpoint.Manager.BP> bps : allCombinations(Arrays.asList(breakpoints))) {
+ System.out.println("\tBreaking on " + bps);
+ for (Runnable test : invokes) {
+ MANAGER.clearAllBreakpoints();
+ MANAGER.setBreakpoints(bps.toArray(new Breakpoint.Manager.BP[0]));
+ test.run();
+ }
+ }
+ }
+}
diff --git a/test/994-breakpoint-line/expected.txt b/test/994-breakpoint-line/expected.txt
new file mode 100644
index 0000000000..5899659b3c
--- /dev/null
+++ b/test/994-breakpoint-line/expected.txt
@@ -0,0 +1,34 @@
+Breaking on line: 29 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=29
+ argument was true
+Breaking on line: 29 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=29
+ argument was false
+Breaking on line: 30 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=30
+ argument was true
+Breaking on line: 30 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=30
+ argument was false
+Breaking on line: 31 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=31
+ argument was true
+Breaking on line: 31 calling with arg: false
+ argument was false
+Breaking on line: 33 calling with arg: true
+ argument was true
+Breaking on line: 33 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=33
+ argument was false
+Breaking on line: 35 calling with arg: true
+ argument was true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=35
+Breaking on line: 35 calling with arg: false
+ argument was false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=35
+Breaking on line: 36 calling with arg: true
+ argument was true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=36
+Breaking on line: 36 calling with arg: false
+ argument was false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=36
diff --git a/test/994-breakpoint-line/info.txt b/test/994-breakpoint-line/info.txt
new file mode 100644
index 0000000000..210dea0471
--- /dev/null
+++ b/test/994-breakpoint-line/info.txt
@@ -0,0 +1,5 @@
+Test basic JVMTI breakpoint functionality.
+
+This test ensures we can place breakpoints on particular lines of a method. It
+sets breakpoints on each line in turn of a function with multiple execution
+paths and then runs the function, receiving the breakpoint events.
diff --git a/test/994-breakpoint-line/run b/test/994-breakpoint-line/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/994-breakpoint-line/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/994-breakpoint-line/src/Main.java b/test/994-breakpoint-line/src/Main.java
new file mode 100644
index 0000000000..39cfeb3ee4
--- /dev/null
+++ b/test/994-breakpoint-line/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test994.run();
+ }
+}
diff --git a/test/994-breakpoint-line/src/art/Breakpoint.java b/test/994-breakpoint-line/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/994-breakpoint-line/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/994-breakpoint-line/src/art/Test994.java b/test/994-breakpoint-line/src/art/Test994.java
new file mode 100644
index 0000000000..6a1c354b39
--- /dev/null
+++ b/test/994-breakpoint-line/src/art/Test994.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test994 {
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ System.out.println("\targument was true");
+ } else {
+ System.out.println("\targument was false");
+ }
+ doNothing();
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println(
+ "\tBreakpoint reached: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test994.class,
+ Test994.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method multipath_method = Test994.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(multipath_method);
+
+ // Make sure everything is in the same order.
+ Arrays.sort(lines);
+
+ boolean[] values = new boolean[] { true, false };
+
+ for (Breakpoint.LineNumber line : lines) {
+ MANAGER.clearAllBreakpoints();
+ MANAGER.setBreakpoint(multipath_method, line.location);
+ for (boolean arg : values) {
+ System.out.println("Breaking on line: " + line.line + " calling with arg: " + arg);
+ doMultiPath(arg);
+ }
+ }
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+}
diff --git a/test/995-breakpoints-throw/expected.txt b/test/995-breakpoints-throw/expected.txt
new file mode 100644
index 0000000000..a565b7cf14
--- /dev/null
+++ b/test/995-breakpoints-throw/expected.txt
@@ -0,0 +1,34 @@
+Test "call Test995::breakpoint": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "call Test995::breakpoint": No error caught with handler "do nothing"
+Test "call Test995::breakpoint": Finished running with handler "do nothing"
+Test "call Test995::breakpointCatch": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpointCatch() @ line=48
+Test "call Test995::breakpointCatch": No error caught with handler "do nothing"
+Test "call Test995::breakpointCatch": Finished running with handler "do nothing"
+Test "call Test995::breakpointCatchLate": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpointCatchLate() @ line=38
+Test "call Test995::breakpointCatchLate": No error caught with handler "do nothing"
+Test "call Test995::breakpointCatchLate": Finished running with handler "do nothing"
+Test "catch subroutine Test995::breakpoint": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "catch subroutine Test995::breakpoint": No error caught with handler "do nothing"
+Test "catch subroutine Test995::breakpoint": Finished running with handler "do nothing"
+Test "call Test995::breakpoint": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "call Test995::breakpoint": Caught error java.lang.Error:"throwing error!" with handler "throw"
+Test "call Test995::breakpoint": Finished running with handler "throw"
+Test "call Test995::breakpointCatch": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpointCatch() @ line=48
+Caught java.lang.Error: "throwing error!"
+Test "call Test995::breakpointCatch": No error caught with handler "throw"
+Test "call Test995::breakpointCatch": Finished running with handler "throw"
+Test "call Test995::breakpointCatchLate": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpointCatchLate() @ line=38
+Test "call Test995::breakpointCatchLate": Caught error java.lang.Error:"throwing error!" with handler "throw"
+Test "call Test995::breakpointCatchLate": Finished running with handler "throw"
+Test "catch subroutine Test995::breakpoint": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Caught java.lang.Error:"throwing error!"
+Test "catch subroutine Test995::breakpoint": No error caught with handler "throw"
+Test "catch subroutine Test995::breakpoint": Finished running with handler "throw"
diff --git a/test/995-breakpoints-throw/info.txt b/test/995-breakpoints-throw/info.txt
new file mode 100644
index 0000000000..80f9cf94bf
--- /dev/null
+++ b/test/995-breakpoints-throw/info.txt
@@ -0,0 +1,6 @@
+Test basic JVMTI breakpoint functionality.
+
+Tests that it is possible to throw exceptions while handling breakpoint events
+and that they are handled appropriately. This includes checking that it is
+possible for the method being breakpointed to catch exceptions thrown by the
+handler.
diff --git a/test/995-breakpoints-throw/run b/test/995-breakpoints-throw/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/995-breakpoints-throw/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/995-breakpoints-throw/src/Main.java b/test/995-breakpoints-throw/src/Main.java
new file mode 100644
index 0000000000..6f80b43255
--- /dev/null
+++ b/test/995-breakpoints-throw/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test995.run();
+ }
+}
diff --git a/test/995-breakpoints-throw/src/art/Breakpoint.java b/test/995-breakpoints-throw/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/995-breakpoints-throw/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/995-breakpoints-throw/src/art/Test995.java b/test/995-breakpoints-throw/src/art/Test995.java
new file mode 100644
index 0000000000..a4023fb80a
--- /dev/null
+++ b/test/995-breakpoints-throw/src/art/Test995.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test995 {
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static BreakpointHandler HANDLER = null;
+
+ public static void doNothing() { }
+
+ public static interface BreakpointHandler {
+ public void breakpointReached(Executable e, long loc);
+ }
+
+ public static void breakpoint() {
+ return;
+ }
+
+ public static void breakpointCatchLate() {
+ doNothing();
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void breakpointCatch() {
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ HANDLER.breakpointReached(e, loc);
+ }
+
+
+ public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
+ return new BreakpointHandler() {
+ public String toString() {
+ return name;
+ }
+ public void breakpointReached(Executable e, long loc) {
+ h.breakpointReached(e, loc);
+ }
+ };
+ }
+
+ public static Runnable makeTest(String name, Runnable test) {
+ return new Runnable() {
+ public String toString() { return name; }
+ public void run() { test.run(); }
+ };
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test995.class,
+ Test995.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
+ Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
+ Method breakpoint_catch_late_method = Test995.class.getDeclaredMethod("breakpointCatchLate");
+ MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_late_method, Breakpoint.getStartLocation(breakpoint_catch_late_method));
+
+ BreakpointHandler[] handlers = new BreakpointHandler[] {
+ makeHandler("do nothing", (e, l) -> {}),
+ makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
+ };
+
+ Runnable[] tests = new Runnable[] {
+ makeTest("call Test995::breakpoint", Test995::breakpoint),
+ makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
+ makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
+ makeTest("catch subroutine Test995::breakpoint",
+ () -> {
+ try {
+ breakpoint();
+ } catch (Throwable t) {
+ System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(), t.getMessage());
+ }
+ }),
+ };
+
+ for (BreakpointHandler handler : handlers) {
+ for (Runnable test : tests) {
+ try {
+ HANDLER = handler;
+ System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
+ test, handler);
+ test.run();
+ System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
+ test, handler);
+ } catch (Throwable e) {
+ System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+ test, e.getClass().getName(), e.getMessage(), handler);
+ }
+ System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
+ HANDLER = null;
+ }
+ }
+
+ MANAGER.clearAllBreakpoints();
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+}
diff --git a/test/996-breakpoint-obsolete/expected.txt b/test/996-breakpoint-obsolete/expected.txt
new file mode 100644
index 0000000000..e0d419e3f5
--- /dev/null
+++ b/test/996-breakpoint-obsolete/expected.txt
@@ -0,0 +1,14 @@
+Initially setting breakpoint to line 42
+Running transform without redefinition.
+Should be after first breakpoint.
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=42
+Running transform with redefinition.
+Redefining calling function!
+Setting breakpoint on now obsolete method to line 40
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=40
+Should be after first breakpoint.
+Running transform post redefinition. Should not hit any breakpoints.
+Doing nothing transformed
+Setting initial breakpoint on redefined method.
+Doing nothing transformed
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=8
diff --git a/test/996-breakpoint-obsolete/info.txt b/test/996-breakpoint-obsolete/info.txt
new file mode 100644
index 0000000000..58536acece
--- /dev/null
+++ b/test/996-breakpoint-obsolete/info.txt
@@ -0,0 +1,4 @@
+Test JVMTI breakpoint/obsolete method interaction.
+
+This checks that redefining a class will clear breakpoints on the class's
+methods and that it is possible to set breakpoints on obsolete methods.
diff --git a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
new file mode 100644
index 0000000000..b6a67e4a08
--- /dev/null
+++ b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test996ObsoleteBreakpoints {
+
+static constexpr jint kNumFrames = 10;
+
+static jmethodID GetFirstObsoleteMethod(JNIEnv* env, jvmtiEnv* jvmti_env) {
+ jint frame_count;
+ jvmtiFrameInfo frames[kNumFrames];
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetStackTrace(nullptr, // current thread
+ 0,
+ kNumFrames,
+ frames,
+ &frame_count))) {
+ return nullptr;
+ }
+ for (jint i = 0; i < frame_count; i++) {
+ jboolean is_obsolete = false;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->IsMethodObsolete(frames[i].method, &is_obsolete))) {
+ return nullptr;
+ }
+ if (is_obsolete) {
+ return frames[i].method;
+ }
+ }
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find obsolete method!");
+ return nullptr;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test996_setBreakpointOnObsoleteMethod(
+ JNIEnv* env, jclass k ATTRIBUTE_UNUSED, jlong loc) {
+ jmethodID method = GetFirstObsoleteMethod(env, jvmti_env);
+ if (method == nullptr) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, loc));
+}
+
+} // namespace Test996ObsoleteBreakpoints
+} // namespace art
diff --git a/test/996-breakpoint-obsolete/run b/test/996-breakpoint-obsolete/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/996-breakpoint-obsolete/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/996-breakpoint-obsolete/src/Main.java b/test/996-breakpoint-obsolete/src/Main.java
new file mode 100644
index 0000000000..1b9b0a9b4b
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test996.run();
+ }
+}
diff --git a/test/996-breakpoint-obsolete/src/art/Breakpoint.java b/test/996-breakpoint-obsolete/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/988-redefine-use-after-free/src-ex/art/Redefinition.java b/test/996-breakpoint-obsolete/src/art/Redefinition.java
index 56d2938a01..56d2938a01 100644
--- a/test/988-redefine-use-after-free/src-ex/art/Redefinition.java
+++ b/test/996-breakpoint-obsolete/src/art/Redefinition.java
diff --git a/test/996-breakpoint-obsolete/src/art/Test996.java b/test/996-breakpoint-obsolete/src/art/Test996.java
new file mode 100644
index 0000000000..f3166c33c7
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Test996.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Test996 {
+ // The line we are going to break on. This should be the println in the Transform class. We set a
+ // breakpoint here after we have redefined the class.
+ public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
+
+ // The line we initially set a breakpoint on. This should be the doNothing call. This should be
+ // cleared by the redefinition and should only be caught on the initial run.
+ public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
+
+ // A function that doesn't do anything. Used for giving places to break on in a function.
+ public static void doNothing() {}
+
+ public static final class Transform {
+ public void run(Runnable r) {
+ r.run();
+ // Make sure we don't change anything above this line to keep all the breakpoint stuff
+ // working. We will be putting a breakpoint before this line in the runnable.
+ System.out.println("Should be after first breakpoint.");
+ // This is set as a breakpoint prior to redefinition. It should not be hit.
+ doNothing();
+ }
+ }
+
+ /* ******************************************************************************************** */
+ // Try to keep all edits to this file below the above line. If edits need to be made above this
+ // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
+ // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
+
+ public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
+
+ // The base64 encoding of the following class. The redefined 'run' method should have the same
+ // instructions as the original. This means that the locations of each line should stay the same
+ // and the set of valid locations will not change. We use this to ensure that breakpoints are
+ // removed from the redefined method.
+ // public static final class Transform {
+ // public void run(Runnable r) {
+ // r.run();
+ // System.out.println("Doing nothing transformed");
+ // doNothing(); // try to catch non-removed breakpoints
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
+ "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
+ "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
+ "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
+ "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
+ "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
+ "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
+ "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
+ "AAEABwAZABwAGQ==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
+ "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
+ "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
+ "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
+ "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
+ "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
+ "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
+ "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
+ "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
+ "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
+ "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
+ "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
+ "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
+ "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
+ "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
+ "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
+ "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
+ "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ int line = Breakpoint.locationToLine(e, loc);
+ if (line == -1 && e.getName().equals("run") && e.getDeclaringClass().equals(Transform.class)) {
+ // RI always reports line = -1 for obsolete methods. Just replace it with the real line for
+ // consistency.
+ line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ }
+ System.out.println("Breakpoint reached: " + e + " @ line=" + line);
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test996.class,
+ Test996.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Transform t = new Transform();
+ Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
+ final long obsolete_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+
+ System.out.println("Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ long initial_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
+
+ System.out.println("Running transform without redefinition.");
+ t.run(() -> {});
+
+ System.out.println("Running transform with redefinition.");
+ t.run(() -> {
+ System.out.println("Redefining calling function!");
+ // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("Setting breakpoint on now obsolete method to line " +
+ TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
+ });
+ System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
+ t.run(() -> {});
+
+ System.out.println("Setting initial breakpoint on redefined method.");
+ long final_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method,
+ TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
+ t.run(() -> {});
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+
+ public static native void setBreakpointOnObsoleteMethod(long location);
+}
diff --git a/test/997-single-step/expected.txt b/test/997-single-step/expected.txt
new file mode 100644
index 0000000000..69c554ca7f
--- /dev/null
+++ b/test/997-single-step/expected.txt
@@ -0,0 +1,12 @@
+Stepping through doMultiPath(true)
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=41
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=42
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=43
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=47
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=48
+Stepping through doMultiPath(false)
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=41
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=42
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=45
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=47
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=48
diff --git a/test/997-single-step/info.txt b/test/997-single-step/info.txt
new file mode 100644
index 0000000000..e4a584e46f
--- /dev/null
+++ b/test/997-single-step/info.txt
@@ -0,0 +1,3 @@
+Test basic JVMTI single step functionality.
+
+Ensures that we can receive single step events from JVMTI.
diff --git a/test/997-single-step/run b/test/997-single-step/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/997-single-step/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/997-single-step/src/Main.java b/test/997-single-step/src/Main.java
new file mode 100644
index 0000000000..1927f04d50
--- /dev/null
+++ b/test/997-single-step/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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) throws Exception {
+ art.Test997.run();
+ }
+}
diff --git a/test/997-single-step/src/art/Breakpoint.java b/test/997-single-step/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/997-single-step/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/997-single-step/src/art/Test997.java b/test/997-single-step/src/art/Test997.java
new file mode 100644
index 0000000000..a7a522dcca
--- /dev/null
+++ b/test/997-single-step/src/art/Test997.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test997 {
+ static final int NO_LAST_LINE_NUMBER = -1;
+ static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ static Method DO_MULTIPATH_METHOD;
+
+ static {
+ try {
+ DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+ } catch (Exception e) {
+ throw new Error("could not find method doMultiPath", e);
+ }
+ }
+
+ // Function that acts simply to ensure there are multiple lines.
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ doNothing();
+ } else {
+ doNothing();
+ }
+ doNothing();
+ }
+
+ public static void notifySingleStep(Thread thr, Executable e, long loc) {
+ if (!e.equals(DO_MULTIPATH_METHOD)) {
+ // Only report steps in doMultiPath
+ return;
+ }
+ int cur_line = Breakpoint.locationToLine(e, loc);
+ // Only report anything when the line number changes. This is so we can run this test against
+ // both the RI and ART and also to prevent front-end compiler changes from affecting output.
+ if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
+ LAST_LINE_NUMBER = cur_line;
+ System.out.println("Single step: " + e + " @ line=" + cur_line);
+ }
+ }
+
+ public static void resetTest() {
+ LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ }
+
+ public static void run() throws Exception {
+ boolean[] values = new boolean[] { true, false };
+ Trace.enableSingleStepTracing(Test997.class,
+ Test997.class.getDeclaredMethod(
+ "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+ for (boolean arg : values) {
+ System.out.println("Stepping through doMultiPath(" + arg + ")");
+ resetTest();
+ doMultiPath(arg);
+ }
+
+ Trace.disableTracing(Thread.currentThread());
+ }
+}
diff --git a/test/997-single-step/src/art/Trace.java b/test/997-single-step/src/art/Trace.java
new file mode 100644
index 0000000000..ba3d397b0b
--- /dev/null
+++ b/test/997-single-step/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/998-redefine-use-after-free/expected.txt b/test/998-redefine-use-after-free/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/998-redefine-use-after-free/expected.txt
diff --git a/test/988-redefine-use-after-free/info.txt b/test/998-redefine-use-after-free/info.txt
index 2b683dd75e..2b683dd75e 100644
--- a/test/988-redefine-use-after-free/info.txt
+++ b/test/998-redefine-use-after-free/info.txt
diff --git a/test/998-redefine-use-after-free/run b/test/998-redefine-use-after-free/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/998-redefine-use-after-free/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# 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.
+
+./default-run "$@" --jvmti
diff --git a/test/988-redefine-use-after-free/src-ex/DexCacheSmash.java b/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
index 2193a631cd..2193a631cd 100644
--- a/test/988-redefine-use-after-free/src-ex/DexCacheSmash.java
+++ b/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
diff --git a/test/998-redefine-use-after-free/src-ex/art/Redefinition.java b/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/988-redefine-use-after-free/src/Main.java b/test/998-redefine-use-after-free/src/Main.java
index d88c471a07..cd3babf46f 100644
--- a/test/988-redefine-use-after-free/src/Main.java
+++ b/test/998-redefine-use-after-free/src/Main.java
@@ -17,7 +17,7 @@
import java.lang.reflect.*;
public class Main {
- public static final String TEST_NAME = "988-redefine-use-after-free";
+ public static final String TEST_NAME = "998-redefine-use-after-free";
public static final int REPS = 1000;
public static final int STEP = 100;
diff --git a/test/Android.bp b/test/Android.bp
index 23ffc7e5a3..591684b887 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -250,7 +250,10 @@ art_cc_defaults {
"ti-agent/jni_binder.cc",
"ti-agent/jvmti_helper.cc",
"ti-agent/test_env.cc",
+ "ti-agent/breakpoint_helper.cc",
"ti-agent/common_helper.cc",
+ "ti-agent/redefinition_helper.cc",
+ "ti-agent/trace_helper.cc",
// This is the list of non-special OnLoad things and excludes BCI and anything that depends
// on ART internals.
"903-hello-tagging/tagging.cc",
@@ -281,6 +284,10 @@ art_cc_defaults {
"989-method-trace-throw/method_trace.cc",
"991-field-trace-2/field_trace.cc",
"992-source-data/source_file.cc",
+ "993-breakpoints/breakpoints.cc",
+ "996-breakpoint-obsolete/obsolete_breakpoints.cc",
+ "1900-track-alloc/alloc.cc",
+ "1901-get-bytecodes/bytecodes.cc",
],
shared_libs: [
"libbase",
@@ -440,6 +447,7 @@ art_cc_test_library {
"art_debug_defaults",
"art_defaults",
],
+ header_libs: ["libnativebridge-dummy-headers"],
srcs: ["115-native-bridge/nativebridge.cc"],
target: {
android: {
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
deleted file mode 100644
index 60ce6c7003..0000000000
--- a/test/Android.run-test-jvmti-java-library.mk
+++ /dev/null
@@ -1,159 +0,0 @@
-#
-# Copyright (C) 2017 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 $(CLEAR_VARS)
-
-# shim classes. We use one that exposes the common functionality.
-LOCAL_SHIM_CLASSES := \
- 902-hello-transformation/src/art/Redefinition.java \
- 903-hello-tagging/src/art/Main.java \
-
-LOCAL_SRC_FILES := $(LOCAL_SHIM_CLASSES)
-
-# Actual test classes.
-LOCAL_SRC_FILES += \
- 901-hello-ti-agent/src/art/Test901.java \
- 902-hello-transformation/src/art/Test902.java \
- 903-hello-tagging/src/art/Test903.java \
- 904-object-allocation/src/art/Test904.java \
- 905-object-free/src/art/Test905.java \
- 906-iterate-heap/src/art/Test906.java \
- 907-get-loaded-classes/src/art/Test907.java \
- 907-get-loaded-classes/src/art/Cerr.java \
- 908-gc-start-finish/src/art/Test908.java \
- 910-methods/src/art/Test910.java \
- 911-get-stack-trace/src/art/Test911.java \
- 911-get-stack-trace/src/art/AllTraces.java \
- 911-get-stack-trace/src/art/ControlData.java \
- 911-get-stack-trace/src/art/Frames.java \
- 911-get-stack-trace/src/art/OtherThread.java \
- 911-get-stack-trace/src/art/PrintThread.java \
- 911-get-stack-trace/src/art/Recurse.java \
- 911-get-stack-trace/src/art/SameThread.java \
- 911-get-stack-trace/src/art/ThreadListTraces.java \
- 912-classes/src-art/art/Test912.java \
- 912-classes/src-art/art/DexData.java \
- 913-heaps/src/art/Test913.java \
- 914-hello-obsolescence/src/art/Test914.java \
- 915-obsolete-2/src/art/Test915.java \
- 917-fields-transformation/src/art/Test917.java \
- 918-fields/src/art/Test918.java \
- 919-obsolete-fields/src/art/Test919.java \
- 920-objects/src/art/Test920.java \
- 922-properties/src/art/Test922.java \
- 923-monitors/src/art/Test923.java \
- 924-threads/src/art/Test924.java \
- 925-threadgroups/src/art/Test925.java \
- 926-multi-obsolescence/src/art/Test926.java \
- 927-timers/src/art/Test927.java \
- 928-jni-table/src/art/Test928.java \
- 930-hello-retransform/src/art/Test930.java \
- 931-agent-thread/src/art/Test931.java \
- 932-transform-saves/src/art/Test932.java \
- 933-misc-events/src/art/Test933.java \
- 940-recursive-obsolete/src/art/Test940.java \
- 942-private-recursive/src/art/Test942.java \
- 944-transform-classloaders/src/art/Test944.java \
- 945-obsolete-native/src/art/Test945.java \
- 947-reflect-method/src/art/Test947.java \
- 951-threaded-obsolete/src/art/Test951.java \
- 981-dedup-original-dex/src-art/art/Test981.java \
- 982-ok-no-retransform/src/art/Test982.java \
- 984-obsolete-invoke/src/art/Test984.java \
- 985-re-obsolete/src/art/Test985.java \
- 986-native-method-bind/src/art/Test986.java \
-
-JVMTI_RUN_TEST_GENERATED_NUMBERS := \
- 901 \
- 902 \
- 903 \
- 904 \
- 905 \
- 906 \
- 907 \
- 908 \
- 910 \
- 911 \
- 912 \
- 913 \
- 914 \
- 915 \
- 917 \
- 918 \
- 919 \
- 920 \
- 922 \
- 923 \
- 924 \
- 925 \
- 926 \
- 927 \
- 928 \
- 930 \
- 931 \
- 932 \
- 933 \
- 940 \
- 942 \
- 944 \
- 945 \
- 947 \
- 951 \
- 981 \
- 982 \
- 984 \
- 985 \
- 986 \
-
-# Try to enforce that the directories correspond to the Java files we pull in.
-JVMTI_RUN_TEST_DIR_CHECK := $(sort $(foreach DIR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS), \
- $(filter $(DIR)%,$(LOCAL_SRC_FILES))))
-ifneq ($(sort $(LOCAL_SRC_FILES)),$(JVMTI_RUN_TEST_DIR_CHECK))
- $(error Missing file, compare $(sort $(LOCAL_SRC_FILES)) with $(JVMTI_RUN_TEST_DIR_CHECK))
-endif
-
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_MODULE_TAGS := optional
-LOCAL_JAVA_LANGUAGE_VERSION := 1.8
-LOCAL_MODULE := run-test-jvmti-java
-
-GENERATED_SRC_DIR := $(call local-generated-sources-dir)
-JVMTI_RUN_TEST_GENERATED_FILES := \
- $(foreach NR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS),$(GENERATED_SRC_DIR)/results.$(NR).expected.txt)
-
-define GEN_JVMTI_RUN_TEST_GENERATED_FILE
-
-GEN_INPUT := $(wildcard $(LOCAL_PATH)/$(1)*/expected.txt)
-GEN_OUTPUT := $(GENERATED_SRC_DIR)/results.$(1).expected.txt
-$$(GEN_OUTPUT): $$(GEN_INPUT)
- cp $$< $$@
-
-GEN_INPUT :=
-GEN_OUTPUT :=
-
-endef
-
-$(foreach NR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS),\
- $(eval $(call GEN_JVMTI_RUN_TEST_GENERATED_FILE,$(NR))))
-LOCAL_JAVA_RESOURCE_FILES := $(JVMTI_RUN_TEST_GENERATED_FILES)
-
-# We only want to depend on libcore.
-LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-all
-
-include $(BUILD_JAVA_LIBRARY)
diff --git a/test/ForClassLoaderA/Classes.java b/test/ForClassLoaderA/Classes.java
new file mode 100644
index 0000000000..a65ef64161
--- /dev/null
+++ b/test/ForClassLoaderA/Classes.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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 DefinedInA {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInAD {
+}
+
diff --git a/test/ForClassLoaderB/Classes.java b/test/ForClassLoaderB/Classes.java
new file mode 100644
index 0000000000..8c85ed5fc0
--- /dev/null
+++ b/test/ForClassLoaderB/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 DefinedInB {
+}
+
+class DefinedInAB {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInBD {
+}
diff --git a/test/ForClassLoaderC/Classes.java b/test/ForClassLoaderC/Classes.java
new file mode 100644
index 0000000000..7b9e83ffff
--- /dev/null
+++ b/test/ForClassLoaderC/Classes.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 DefinedInC {
+}
+
+class DefinedInAC {
+}
+
+class DefinedInABC {
+}
+
+class DefinedInBC {
+}
+
+class DefinedInCD {
+}
diff --git a/test/ForClassLoaderD/Classes.java b/test/ForClassLoaderD/Classes.java
new file mode 100644
index 0000000000..b34177f05f
--- /dev/null
+++ b/test/ForClassLoaderD/Classes.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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 DefinedInD {
+}
+
+class DefinedInAD {
+}
+
+class DefinedInBD {
+}
+
+class DefinedInCD {
+}
diff --git a/test/dexdump/invoke-custom.dex b/test/dexdump/invoke-custom.dex
index 67261cad79..dab6f0f0d6 100644
--- a/test/dexdump/invoke-custom.dex
+++ b/test/dexdump/invoke-custom.dex
Binary files differ
diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst
index 3540bd10d5..9037c28990 100644
--- a/test/dexdump/invoke-custom.lst
+++ b/test/dexdump/invoke-custom.lst
@@ -1,6 +1,35 @@
#invoke-custom.dex
-0x000003fc 8 com.android.jack.java7.invokecustom.test004.Tests <init> ()V Tests.java 35
-0x00000414 6 com.android.jack.java7.invokecustom.test004.Tests add (II)I Tests.java 55
-0x0000042c 166 com.android.jack.java7.invokecustom.test004.Tests linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; Tests.java 62
-0x000004e4 24 com.android.jack.java7.invokecustom.test004.Tests main ([Ljava/lang/String;)V Tests.java 82
-0x0000050c 22 com.android.jack.java7.invokecustom.test004.Tests test ()V Tests.java 78
+0x000009a0 8 invokecustom.Super <init> ()V InvokeCustom.java 29
+0x000009b8 16 invokecustom.Super targetMethodTest4 ()V InvokeCustom.java 31
+0x000009d8 8 invokecustom.InvokeCustom <clinit> ()V InvokeCustom.java 102
+0x000009f0 14 invokecustom.InvokeCustom <init> ()V InvokeCustom.java 39
+0x00000a10 74 invokecustom.InvokeCustom <init> (I)V InvokeCustom.java 40
+0x00000a6c 72 invokecustom.InvokeCustom bsmCreateCallSite (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; InvokeCustom.java 160
+0x00000ac4 58 invokecustom.InvokeCustom bsmLookupStatic (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; InvokeCustom.java 142
+0x00000b10 164 invokecustom.InvokeCustom bsmLookupStaticWithExtraArgs (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite; InvokeCustom.java 151
+0x00000bc4 270 invokecustom.InvokeCustom bsmLookupTest9 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite; InvokeCustom.java 170
+0x00000ce4 164 invokecustom.InvokeCustom checkFieldTest9 (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V InvokeCustom.java 120
+0x00000d98 160 invokecustom.InvokeCustom checkStaticFieldTest9 (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V InvokeCustom.java 107
+0x00000e48 22 invokecustom.InvokeCustom lambda$lambdaTest$0 (Ljava/lang/String;)Z InvokeCustom.java 192
+0x00000e70 142 invokecustom.InvokeCustom lambdaTest ()V InvokeCustom.java 191
+0x00000f10 56 invokecustom.InvokeCustom main ([Ljava/lang/String;)V InvokeCustom.java -1
+0x00000f58 16 invokecustom.InvokeCustom targetMethodTest1 ()V InvokeCustom.java 45
+0x00000f78 92 invokecustom.InvokeCustom targetMethodTest2 (ZBCSIFJDLjava/lang/String;)V InvokeCustom.java 50
+0x00000fe4 16 invokecustom.InvokeCustom targetMethodTest3 ()V InvokeCustom.java 62
+0x00001004 166 invokecustom.InvokeCustom targetMethodTest5 (III)I InvokeCustom.java 72
+0x000010bc 170 invokecustom.InvokeCustom targetMethodTest6 (JJJ)J InvokeCustom.java 81
+0x00001178 172 invokecustom.InvokeCustom targetMethodTest7 (FFD)D InvokeCustom.java 90
+0x00001234 50 invokecustom.InvokeCustom targetMethodTest8 (Ljava/lang/String;)V InvokeCustom.java 99
+0x00001278 16 invokecustom.InvokeCustom targetMethodTest9 ()V InvokeCustom.java 133
+0x00001298 8 invokecustom.InvokeCustom test1 ()V InvokeCustom.java -1
+0x000012b0 54 invokecustom.InvokeCustom test2 ()V InvokeCustom.java -1
+0x000012f8 8 invokecustom.InvokeCustom test3 ()V InvokeCustom.java -1
+0x00001310 18 invokecustom.InvokeCustom test4 ()V InvokeCustom.java -1
+0x00001334 70 invokecustom.InvokeCustom test5 ()V InvokeCustom.java -1
+0x0000138c 88 invokecustom.InvokeCustom test6 ()V InvokeCustom.java -1
+0x000013f4 80 invokecustom.InvokeCustom test7 ()V InvokeCustom.java -1
+0x00001454 32 invokecustom.InvokeCustom test8 ()V InvokeCustom.java -1
+0x00001484 8 invokecustom.InvokeCustom test9 ()V InvokeCustom.java -1
+0x0000149c 54 invokecustom.InvokeCustom helperMethodTest9 ()V InvokeCustom.java 129
+0x000014e4 16 invokecustom.InvokeCustom run ()V InvokeCustom.java 137
+0x00001504 16 invokecustom.InvokeCustom targetMethodTest4 ()V InvokeCustom.java 68
diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt
index e92549a086..bd3250865b 100644
--- a/test/dexdump/invoke-custom.txt
+++ b/test/dexdump/invoke-custom.txt
@@ -2,253 +2,1424 @@ Processing 'invoke-custom.dex'...
Opened 'invoke-custom.dex', DEX version '038'
DEX file header:
magic : 'dex\n038\0'
-checksum : db57516f
-signature : 57be...ffc4
-file_size : 3276
+checksum : d11a9e29
+signature : 5b54...15c3
+file_size : 8984
header_size : 112
link_size : 0
link_off : 0 (0x000000)
-string_ids_size : 82
+string_ids_size : 165
string_ids_off : 112 (0x000070)
-type_ids_size : 31
-type_ids_off : 440 (0x0001b8)
-proto_ids_size : 16
-proto_ids_off : 564 (0x000234)
+type_ids_size : 38
+type_ids_off : 772 (0x000304)
+proto_ids_size : 51
+proto_ids_off : 924 (0x00039c)
field_ids_size : 3
-field_ids_off : 756 (0x0002f4)
-method_ids_size : 18
-method_ids_off : 780 (0x00030c)
-class_defs_size : 1
-class_defs_off : 932 (0x0003a4)
-data_size : 2304
-data_off : 972 (0x0003cc)
+field_ids_off : 1536 (0x000600)
+method_ids_size : 78
+method_ids_off : 1560 (0x000618)
+class_defs_size : 2
+class_defs_off : 2184 (0x000888)
+data_size : 6552
+data_off : 2432 (0x000980)
Class #0 header:
-class_idx : 10
-access_flags : 1 (0x0001)
-superclass_idx : 15
+class_idx : 8
+access_flags : 1024 (0x0400)
+superclass_idx : 13
interfaces_off : 0 (0x000000)
-source_file_idx : 38
-annotations_off : 1316 (0x000524)
-class_data_off : 3014 (0x000bc6)
-static_fields_size : 1
+source_file_idx : 27
+annotations_off : 0 (0x000000)
+class_data_off : 8589 (0x00218d)
+static_fields_size : 0
instance_fields_size: 0
-direct_methods_size : 4
-virtual_methods_size: 1
-
-Class #0 annotations:
-Annotations on method #1 'add'
- VISIBILITY_BUILD Lcom/android/jack/annotations/CalledByInvokeCustom; argumentTypes={ I I } invokeMethodHandle={ Lcom/android/jack/annotations/LinkerMethodHandle; argumentTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Z B C S I F D Ljava/lang/String; Ljava/lang/Class; J } enclosingType=Lcom/android/jack/java7/invokecustom/test004/Tests; kind=INVOKE_STATIC name="linkerMethod" } methodHandleExtraArgs={ Lcom/android/jack/annotations/Constant; booleanValue={ true } Lcom/android/jack/annotations/Constant; byteValue={ 1 } Lcom/android/jack/annotations/Constant; charValue={ 97 } Lcom/android/jack/annotations/Constant; shortValue={ 1024 } Lcom/android/jack/annotations/Constant; intValue={ 1 } Lcom/android/jack/annotations/Constant; floatValue={ 11.1 } Lcom/android/jack/annotations/Constant; doubleValue={ 2.2 } Lcom/android/jack/annotations/Constant; stringValue={ "Hello" } Lcom/android/jack/annotations/Constant; classValue={ Lcom/android/jack/java7/invokecustom/test004/Tests; } Lcom/android/jack/annotations/Constant; longValue={ 123456789 } } name="add" returnType=I
-Annotations on method #2 'linkerMethod'
- VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "ZBCSIFD" "Ljava/lang/String;" "Ljava/lang/Class" "<*>;J)" "Ljava/lang/invoke/CallSite;" }
- VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
-Annotations on method #4 'test'
- VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
- VISIBILITY_RUNTIME Lorg/junit/Test;
+direct_methods_size : 1
+virtual_methods_size: 2
Class #0 -
- Class descriptor : 'Lcom/android/jack/java7/invokecustom/test004/Tests;'
- Access flags : 0x0001 (PUBLIC)
+ Class descriptor : 'Linvokecustom/Super;'
+ Access flags : 0x0400 (ABSTRACT)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
- #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
- name : 'fieldCallSite'
- type : 'Ljava/lang/invoke/CallSite;'
- access : 0x0009 (PUBLIC STATIC)
Instance fields -
Direct methods -
- #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ #0 : (in Linvokecustom/Super;)
name : '<init>'
type : '()V'
- access : 0x10001 (PUBLIC CONSTRUCTOR)
+ access : 0x10000 (CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
-0003ec: |[0003ec] com.android.jack.java7.invokecustom.test004.Tests.<init>:()V
-0003fc: 7010 0600 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0006
-000402: 0e00 |0003: return-void
+000990: |[000990] invokecustom.Super.<init>:()V
+0009a0: 7010 2b00 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@002b
+0009a6: 0e00 |0003: return-void
catches : (none)
positions :
- 0x0000 line=35
+ 0x0000 line=29
locals :
- 0x0000 - 0x0004 reg=0 this Lcom/android/jack/java7/invokecustom/test004/Tests;
+ 0x0000 - 0x0004 reg=0 this Linvokecustom/Super;
- #1 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
- name : 'add'
- type : '(II)I'
- access : 0x000a (PRIVATE STATIC)
+ Virtual methods -
+ #0 : (in Linvokecustom/Super;)
+ name : 'helperMethodTest9'
+ type : '()V'
+ access : 0x0401 (PUBLIC ABSTRACT)
+ code : (none)
+
+ #1 : (in Linvokecustom/Super;)
+ name : 'targetMethodTest4'
+ type : '()V'
+ access : 0x0001 (PUBLIC)
code -
registers : 3
- ins : 2
+ ins : 1
+ outs : 2
+ insns size : 8 16-bit code units
+0009a8: |[0009a8] invokecustom.Super.targetMethodTest4:()V
+0009b8: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0009bc: 1a01 8b00 |0002: const-string v1, "targetMethodTest4 from Super" // string@008b
+0009c0: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+0009c6: 0e00 |0007: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=31
+ 0x0007 line=32
+ locals :
+ 0x0000 - 0x0008 reg=2 this Linvokecustom/Super;
+
+ source_file_idx : 27 (InvokeCustom.java)
+
+Class #1 header:
+class_idx : 7
+access_flags : 1 (0x0001)
+superclass_idx : 8
+interfaces_off : 5460 (0x001554)
+source_file_idx : 27
+annotations_off : 5396 (0x001514)
+class_data_off : 8607 (0x00219f)
+static_fields_size : 1
+instance_fields_size: 1
+direct_methods_size : 29
+virtual_methods_size: 3
+
+Class #1 annotations:
+Annotations on method #3 'bsmCreateCallSite'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+Annotations on method #4 'bsmLookupStatic'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/NoSuchMethodException; Ljava/lang/IllegalAccessException; }
+Annotations on method #5 'bsmLookupStaticWithExtraArgs'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/NoSuchMethodException; Ljava/lang/IllegalAccessException; }
+Annotations on method #6 'bsmLookupTest9'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+Annotations on method #7 'checkFieldTest9'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+Annotations on method #8 'checkStaticFieldTest9'
+ VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; }
+
+Class #1 -
+ Class descriptor : 'Linvokecustom/InvokeCustom;'
+ Access flags : 0x0001 (PUBLIC)
+ Superclass : 'Linvokecustom/Super;'
+ Interfaces -
+ #0 : 'Ljava/lang/Runnable;'
+ Static fields -
+ #0 : (in Linvokecustom/InvokeCustom;)
+ name : 'staticFieldTest9'
+ type : 'I'
+ access : 0x000a (PRIVATE STATIC)
+ Instance fields -
+ #0 : (in Linvokecustom/InvokeCustom;)
+ name : 'fieldTest9'
+ type : 'F'
+ access : 0x0002 (PRIVATE)
+ Direct methods -
+ #0 : (in Linvokecustom/InvokeCustom;)
+ name : '<clinit>'
+ type : '()V'
+ access : 0x10008 (STATIC CONSTRUCTOR)
+ code -
+ registers : 1
+ ins : 0
outs : 0
- insns size : 3 16-bit code units
-000404: |[000404] com.android.jack.java7.invokecustom.test004.Tests.add:(II)I
-000414: 9000 0102 |0000: add-int v0, v1, v2
-000418: 0f00 |0002: return v0
+ insns size : 4 16-bit code units
+0009c8: |[0009c8] invokecustom.InvokeCustom.<clinit>:()V
+0009d8: 1200 |0000: const/4 v0, #int 0 // #0
+0009da: 6700 0100 |0001: sput v0, Linvokecustom/InvokeCustom;.staticFieldTest9:I // field@0001
+0009de: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=102
+ locals :
+
+ #1 : (in Linvokecustom/InvokeCustom;)
+ name : '<init>'
+ type : '()V'
+ access : 0x10001 (PUBLIC CONSTRUCTOR)
+ code -
+ registers : 2
+ ins : 1
+ outs : 1
+ insns size : 7 16-bit code units
+0009e0: |[0009e0] invokecustom.InvokeCustom.<init>:()V
+0009f0: 7010 2000 0100 |0000: invoke-direct {v1}, Linvokecustom/Super;.<init>:()V // method@0020
+0009f6: 1200 |0003: const/4 v0, #int 0 // #0
+0009f8: 5910 0000 |0004: iput v0, v1, Linvokecustom/InvokeCustom;.fieldTest9:F // field@0000
+0009fc: 0e00 |0006: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=39
+ 0x0003 line=115
+ 0x0006 line=39
+ locals :
+ 0x0000 - 0x0007 reg=1 this Linvokecustom/InvokeCustom;
+
+ #2 : (in Linvokecustom/InvokeCustom;)
+ name : '<init>'
+ type : '(I)V'
+ access : 0x10001 (PUBLIC CONSTRUCTOR)
+ code -
+ registers : 5
+ ins : 2
+ outs : 2
+ insns size : 37 16-bit code units
+000a00: |[000a00] invokecustom.InvokeCustom.<init>:(I)V
+000a10: 7010 2000 0300 |0000: invoke-direct {v3}, Linvokecustom/Super;.<init>:()V // method@0020
+000a16: 1200 |0003: const/4 v0, #int 0 // #0
+000a18: 5930 0000 |0004: iput v0, v3, Linvokecustom/InvokeCustom;.fieldTest9:F // field@0000
+000a1c: 6200 0200 |0006: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000a20: 2201 1000 |0008: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+000a24: 7010 3000 0100 |000a: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000a2a: 1a02 1a00 |000d: const-string v2, "InvokeCustom.<init>(" // string@001a
+000a2e: 6e20 3600 2100 |000f: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000a34: 0c01 |0012: move-result-object v1
+000a36: 6e20 3300 4100 |0013: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+000a3c: 0c01 |0016: move-result-object v1
+000a3e: 1a02 0800 |0017: const-string v2, ")" // string@0008
+000a42: 6e20 3600 2100 |0019: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000a48: 0c01 |001c: move-result-object v1
+000a4a: 6e10 3700 0100 |001d: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000a50: 0c01 |0020: move-result-object v1
+000a52: 6e20 2900 1000 |0021: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000a58: 0e00 |0024: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=40
+ 0x0003 line=115
+ 0x0006 line=41
+ 0x0024 line=42
+ locals :
+ 0x0000 - 0x0025 reg=3 this Linvokecustom/InvokeCustom;
+ 0x0000 - 0x0025 reg=4 (null) I
+
+ #3 : (in Linvokecustom/InvokeCustom;)
+ name : 'bsmCreateCallSite'
+ type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 7
+ ins : 4
+ outs : 2
+ insns size : 36 16-bit code units
+000a5c: |[000a5c] invokecustom.InvokeCustom.bsmCreateCallSite:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;
+000a6c: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000a70: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+000a74: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000a7a: 1a02 6000 |0007: const-string v2, "bsmCreateCallSite [" // string@0060
+000a7e: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000a84: 0c01 |000c: move-result-object v1
+000a86: 6e20 3500 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+000a8c: 0c01 |0010: move-result-object v1
+000a8e: 1a02 5900 |0011: const-string v2, "]" // string@0059
+000a92: 6e20 3600 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000a98: 0c01 |0016: move-result-object v1
+000a9a: 6e10 3700 0100 |0017: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000aa0: 0c01 |001a: move-result-object v1
+000aa2: 6e20 2900 1000 |001b: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000aa8: 2200 1400 |001e: new-instance v0, Ljava/lang/invoke/ConstantCallSite; // type@0014
+000aac: 7020 3800 6000 |0020: invoke-direct {v0, v6}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0038
+000ab2: 1100 |0023: return-object v0
+ catches : (none)
+ positions :
+ 0x0000 line=160
+ 0x001e line=161
+ locals :
+ 0x0000 - 0x0024 reg=3 (null) Ljava/lang/invoke/MethodHandles$Lookup;
+ 0x0000 - 0x0024 reg=4 (null) Ljava/lang/String;
+ 0x0000 - 0x0024 reg=5 (null) Ljava/lang/invoke/MethodType;
+ 0x0000 - 0x0024 reg=6 (null) Ljava/lang/invoke/MethodHandle;
+
+ #4 : (in Linvokecustom/InvokeCustom;)
+ name : 'bsmLookupStatic'
+ type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 5
+ ins : 3
+ outs : 4
+ insns size : 29 16-bit code units
+000ab4: |[000ab4] invokecustom.InvokeCustom.bsmLookupStatic:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+000ac4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000ac8: 1a01 6200 |0002: const-string v1, "bsmLookupStatic []" // string@0062
+000acc: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000ad2: 7100 4600 0000 |0007: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0046
+000ad8: 0c00 |000a: move-result-object v0
+000ada: 6e10 4500 0000 |000b: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@0045
+000ae0: 0c01 |000e: move-result-object v1
+000ae2: 6e40 4400 1043 |000f: invoke-virtual {v0, v1, v3, v4}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0044
+000ae8: 0c00 |0012: move-result-object v0
+000aea: 2201 1400 |0013: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0014
+000aee: 6e20 3a00 4000 |0015: invoke-virtual {v0, v4}, Ljava/lang/invoke/MethodHandle;.asType:(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@003a
+000af4: 0c00 |0018: move-result-object v0
+000af6: 7020 3800 0100 |0019: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0038
+000afc: 1101 |001c: return-object v1
+ catches : (none)
+ positions :
+ 0x0000 line=142
+ 0x0007 line=143
+ 0x000b line=144
+ 0x0013 line=145
+ locals :
+ 0x0000 - 0x001d reg=2 (null) Ljava/lang/invoke/MethodHandles$Lookup;
+ 0x0000 - 0x001d reg=3 (null) Ljava/lang/String;
+ 0x0000 - 0x001d reg=4 (null) Ljava/lang/invoke/MethodType;
+
+ #5 : (in Linvokecustom/InvokeCustom;)
+ name : 'bsmLookupStaticWithExtraArgs'
+ type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite;'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 12
+ ins : 9
+ outs : 4
+ insns size : 82 16-bit code units
+000b00: |[000b00] invokecustom.InvokeCustom.bsmLookupStaticWithExtraArgs:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite;
+000b10: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000b14: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+000b18: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000b1e: 1a02 6400 |0007: const-string v2, "bsmLookupStaticWithExtraArgs [" // string@0064
+000b22: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000b28: 0c01 |000c: move-result-object v1
+000b2a: 6e20 3300 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+000b30: 0c01 |0010: move-result-object v1
+000b32: 1a02 0900 |0011: const-string v2, ", " // string@0009
+000b36: 6e20 3600 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000b3c: 0c01 |0016: move-result-object v1
+000b3e: 6e30 3400 7108 |0017: invoke-virtual {v1, v7, v8}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+000b44: 0c01 |001a: move-result-object v1
+000b46: 1a02 0900 |001b: const-string v2, ", " // string@0009
+000b4a: 6e20 3600 2100 |001d: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000b50: 0c01 |0020: move-result-object v1
+000b52: 6e20 3200 9100 |0021: invoke-virtual {v1, v9}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032
+000b58: 0c01 |0024: move-result-object v1
+000b5a: 1a02 0900 |0025: const-string v2, ", " // string@0009
+000b5e: 6e20 3600 2100 |0027: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000b64: 0c01 |002a: move-result-object v1
+000b66: 6e30 3100 a10b |002b: invoke-virtual {v1, v10, v11}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031
+000b6c: 0c01 |002e: move-result-object v1
+000b6e: 1a02 5900 |002f: const-string v2, "]" // string@0059
+000b72: 6e20 3600 2100 |0031: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000b78: 0c01 |0034: move-result-object v1
+000b7a: 6e10 3700 0100 |0035: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000b80: 0c01 |0038: move-result-object v1
+000b82: 6e20 2900 1000 |0039: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000b88: 7100 4600 0000 |003c: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0046
+000b8e: 0c00 |003f: move-result-object v0
+000b90: 6e10 4500 0000 |0040: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@0045
+000b96: 0c01 |0043: move-result-object v1
+000b98: 6e40 4400 1054 |0044: invoke-virtual {v0, v1, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0044
+000b9e: 0c00 |0047: move-result-object v0
+000ba0: 2201 1400 |0048: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0014
+000ba4: 6e20 3a00 5000 |004a: invoke-virtual {v0, v5}, Ljava/lang/invoke/MethodHandle;.asType:(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@003a
+000baa: 0c00 |004d: move-result-object v0
+000bac: 7020 3800 0100 |004e: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0038
+000bb2: 1101 |0051: return-object v1
+ catches : (none)
+ positions :
+ 0x0000 line=151
+ 0x003c line=152
+ 0x0040 line=153
+ 0x0048 line=154
+ locals :
+ 0x0000 - 0x0052 reg=3 (null) Ljava/lang/invoke/MethodHandles$Lookup;
+ 0x0000 - 0x0052 reg=4 (null) Ljava/lang/String;
+ 0x0000 - 0x0052 reg=5 (null) Ljava/lang/invoke/MethodType;
+ 0x0000 - 0x0052 reg=6 (null) I
+ 0x0000 - 0x0052 reg=7 (null) J
+ 0x0000 - 0x0052 reg=9 (null) F
+ 0x0000 - 0x0052 reg=10 (null) D
+
+ #6 : (in Linvokecustom/InvokeCustom;)
+ name : 'bsmLookupTest9'
+ type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 13
+ ins : 10
+ outs : 4
+ insns size : 135 16-bit code units
+000bb4: |[000bb4] invokecustom.InvokeCustom.bsmLookupTest9:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;
+000bc4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000bc8: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+000bcc: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000bd2: 1a02 6600 |0007: const-string v2, "bsmLookupTest9 [" // string@0066
+000bd6: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000bdc: 0c01 |000c: move-result-object v1
+000bde: 6e20 3500 6100 |000d: invoke-virtual {v1, v6}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+000be4: 0c01 |0010: move-result-object v1
+000be6: 1a02 0900 |0011: const-string v2, ", " // string@0009
+000bea: 6e20 3600 2100 |0013: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000bf0: 0c01 |0016: move-result-object v1
+000bf2: 6e20 3500 7100 |0017: invoke-virtual {v1, v7}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+000bf8: 0c01 |001a: move-result-object v1
+000bfa: 1a02 0900 |001b: const-string v2, ", " // string@0009
+000bfe: 6e20 3600 2100 |001d: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000c04: 0c01 |0020: move-result-object v1
+000c06: 6e20 3500 8100 |0021: invoke-virtual {v1, v8}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+000c0c: 0c01 |0024: move-result-object v1
+000c0e: 1a02 0900 |0025: const-string v2, ", " // string@0009
+000c12: 6e20 3600 2100 |0027: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000c18: 0c01 |002a: move-result-object v1
+000c1a: 6e20 3500 9100 |002b: invoke-virtual {v1, v9}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+000c20: 0c01 |002e: move-result-object v1
+000c22: 1a02 5900 |002f: const-string v2, "]" // string@0059
+000c26: 6e20 3600 2100 |0031: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000c2c: 0c01 |0034: move-result-object v1
+000c2e: 6e10 3700 0100 |0035: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000c34: 0c01 |0038: move-result-object v1
+000c36: 6e20 2900 1000 |0039: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000c3c: 6200 0200 |003c: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000c40: 2201 1000 |003e: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+000c44: 7010 3000 0100 |0040: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000c4a: 6e20 3600 4100 |0043: invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000c50: 0c01 |0046: move-result-object v1
+000c52: 1a02 0100 |0047: const-string v2, " " // string@0001
+000c56: 6e20 3600 2100 |0049: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000c5c: 0c01 |004c: move-result-object v1
+000c5e: 6e20 3500 5100 |004d: invoke-virtual {v1, v5}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+000c64: 0c01 |0050: move-result-object v1
+000c66: 6e10 3700 0100 |0051: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000c6c: 0c01 |0054: move-result-object v1
+000c6e: 6e20 2900 1000 |0055: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000c74: 7120 0800 7600 |0058: invoke-static {v6, v7}, Linvokecustom/InvokeCustom;.checkStaticFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V // method@0008
+000c7a: 2200 0700 |005b: new-instance v0, Linvokecustom/InvokeCustom; // type@0007
+000c7e: 7010 0100 0000 |005d: invoke-direct {v0}, Linvokecustom/InvokeCustom;.<init>:()V // method@0001
+000c84: 7030 0700 8009 |0060: invoke-direct {v0, v8, v9}, Linvokecustom/InvokeCustom;.checkFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V // method@0007
+000c8a: fa20 4000 0a00 2700 |0063: invoke-polymorphic {v10, v0}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)V // method@0040, proto@0027
+000c92: 1230 |0067: const/4 v0, #int 3 // #3
+000c94: fa20 4000 0b00 0500 |0068: invoke-polymorphic {v11, v0}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (I)Linvokecustom/InvokeCustom; // method@0040, proto@0005
+000c9c: 0c00 |006c: move-result-object v0
+000c9e: fa20 3b00 0c00 2700 |006d: invoke-polymorphic {v12, v0}, Ljava/lang/invoke/MethodHandle;.invoke:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)V // method@003b, proto@0027
+000ca6: 7100 4600 0000 |0071: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0046
+000cac: 0c00 |0074: move-result-object v0
+000cae: 6e10 4500 0000 |0075: invoke-virtual {v0}, Ljava/lang/invoke/MethodHandles$Lookup;.lookupClass:()Ljava/lang/Class; // method@0045
+000cb4: 0c01 |0078: move-result-object v1
+000cb6: 6e40 4400 1054 |0079: invoke-virtual {v0, v1, v4, v5}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0044
+000cbc: 0c00 |007c: move-result-object v0
+000cbe: 2201 1400 |007d: new-instance v1, Ljava/lang/invoke/ConstantCallSite; // type@0014
+000cc2: 6e20 3a00 5000 |007f: invoke-virtual {v0, v5}, Ljava/lang/invoke/MethodHandle;.asType:(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@003a
+000cc8: 0c00 |0082: move-result-object v0
+000cca: 7020 3800 0100 |0083: invoke-direct {v1, v0}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0038
+000cd0: 1101 |0086: return-object v1
catches : (none)
positions :
- 0x0000 line=55
+ 0x0000 line=170
+ 0x003c line=172
+ 0x0058 line=175
+ 0x005b line=176
+ 0x0060 line=177
+ 0x0063 line=180
+ 0x0067 line=182
+ 0x006d line=183
+ 0x0071 line=185
+ 0x0075 line=186
+ 0x007d line=187
locals :
- 0x0000 - 0x0003 reg=1 (null) I
- 0x0000 - 0x0003 reg=2 (null) I
+ 0x0000 - 0x0087 reg=3 (null) Ljava/lang/invoke/MethodHandles$Lookup;
+ 0x0000 - 0x0087 reg=4 (null) Ljava/lang/String;
+ 0x0000 - 0x0087 reg=5 (null) Ljava/lang/invoke/MethodType;
+ 0x0000 - 0x0087 reg=6 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0087 reg=7 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0087 reg=8 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0087 reg=9 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0087 reg=10 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0087 reg=11 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0087 reg=12 (null) Ljava/lang/invoke/MethodHandle;
- #2 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
- name : 'linkerMethod'
- type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;'
+ #7 : (in Linvokecustom/InvokeCustom;)
+ name : 'checkFieldTest9'
+ type : '(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V'
+ access : 0x0002 (PRIVATE)
+ code -
+ registers : 9
+ ins : 3
+ outs : 3
+ insns size : 82 16-bit code units
+000cd4: |[000cd4] invokecustom.InvokeCustom.checkFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V
+000ce4: 1405 0ff0 6a20 |0000: const v5, #float 1.99e-19 // #206af00f
+000cea: fa20 4000 6700 0100 |0003: invoke-polymorphic {v7, v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)F // method@0040, proto@0001
+000cf2: 0a00 |0007: move-result v0
+000cf4: fa30 4000 6805 2800 |0008: invoke-polymorphic {v8, v6, v5}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;F)V // method@0040, proto@0028
+000cfc: fa20 4000 6700 0100 |000c: invoke-polymorphic {v7, v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (Linvokecustom/InvokeCustom;)F // method@0040, proto@0001
+000d04: 0a01 |0010: move-result v1
+000d06: 6202 0200 |0011: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000d0a: 2203 1000 |0013: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+000d0e: 7010 3000 0300 |0015: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000d14: 1a04 6800 |0018: const-string v4, "checkFieldTest9: old " // string@0068
+000d18: 6e20 3600 4300 |001a: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000d1e: 0c03 |001d: move-result-object v3
+000d20: 6e20 3200 0300 |001e: invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032
+000d26: 0c00 |0021: move-result-object v0
+000d28: 1a03 0700 |0022: const-string v3, " new " // string@0007
+000d2c: 6e20 3600 3000 |0024: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000d32: 0c00 |0027: move-result-object v0
+000d34: 6e20 3200 1000 |0028: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032
+000d3a: 0c00 |002b: move-result-object v0
+000d3c: 1a03 0600 |002c: const-string v3, " expected " // string@0006
+000d40: 6e20 3600 3000 |002e: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000d46: 0c00 |0031: move-result-object v0
+000d48: 6e20 3200 5000 |0032: invoke-virtual {v0, v5}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032
+000d4e: 0c00 |0035: move-result-object v0
+000d50: 1a03 0100 |0036: const-string v3, " " // string@0001
+000d54: 6e20 3600 3000 |0038: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000d5a: 0c00 |003b: move-result-object v0
+000d5c: 6e10 3700 0000 |003c: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000d62: 0c00 |003f: move-result-object v0
+000d64: 6e20 2300 0200 |0040: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@0023
+000d6a: 6202 0200 |0043: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000d6e: 2d00 0105 |0045: cmpl-float v0, v1, v5
+000d72: 3900 0800 |0047: if-nez v0, 004f // +0008
+000d76: 1a00 4400 |0049: const-string v0, "OK" // string@0044
+000d7a: 6e20 2900 0200 |004b: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000d80: 0e00 |004e: return-void
+000d82: 1a00 1100 |004f: const-string v0, "ERROR" // string@0011
+000d86: 28fa |0051: goto 004b // -0006
+ catches : (none)
+ positions :
+ 0x0003 line=120
+ 0x0008 line=121
+ 0x000c line=122
+ 0x0011 line=123
+ 0x0043 line=125
+ 0x004e line=126
+ 0x004f line=125
+ locals :
+ 0x0000 - 0x0052 reg=6 this Linvokecustom/InvokeCustom;
+ 0x0000 - 0x0052 reg=7 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0052 reg=8 (null) Ljava/lang/invoke/MethodHandle;
+
+ #8 : (in Linvokecustom/InvokeCustom;)
+ name : 'checkStaticFieldTest9'
+ type : '(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V'
access : 0x000a (PRIVATE STATIC)
code -
- registers : 24
- ins : 15
- outs : 6
- insns size : 83 16-bit code units
-00041c: |[00041c] com.android.jack.java7.invokecustom.test004.Tests.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
-00042c: 7110 1100 0c00 |0000: invoke-static {v12}, Ljunit/framework/Assert;.assertTrue:(Z)V // method@0011
-000432: 1212 |0003: const/4 v2, #int 1 // #1
-000434: 7120 0d00 d200 |0004: invoke-static {v2, v13}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
-00043a: 1302 6100 |0007: const/16 v2, #int 97 // #61
-00043e: 7120 0a00 e200 |0009: invoke-static {v2, v14}, Ljunit/framework/Assert;.assertEquals:(CC)V // method@000a
-000444: 1302 0004 |000c: const/16 v2, #int 1024 // #400
-000448: 7120 0d00 f200 |000e: invoke-static {v2, v15}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
-00044e: 1212 |0011: const/4 v2, #int 1 // #1
-000450: 0200 1000 |0012: move/from16 v0, v16
-000454: 7120 0d00 0200 |0014: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
-00045a: 1202 |0017: const/4 v2, #int 0 // #0
-00045c: 1403 9a99 3141 |0018: const v3, #float 11.1 // #4131999a
-000462: 0200 1100 |001b: move/from16 v0, v17
-000466: 7130 0c00 0302 |001d: invoke-static {v3, v0, v2}, Ljunit/framework/Assert;.assertEquals:(FFF)V // method@000c
-00046c: 1606 0000 |0020: const-wide/16 v6, #int 0 // #0
-000470: 1802 9a99 9999 9999 0140 |0022: const-wide v2, #double 2.2 // #400199999999999a
-00047a: 0504 1200 |0027: move-wide/from16 v4, v18
-00047e: 7706 0b00 0200 |0029: invoke-static/range {v2, v3, v4, v5, v6, v7}, Ljunit/framework/Assert;.assertEquals:(DDD)V // method@000b
-000484: 1b02 0700 0000 |002c: const-string/jumbo v2, "Hello" // string@00000007
-00048a: 0800 1400 |002f: move-object/from16 v0, v20
-00048e: 7120 1000 0200 |0031: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/String;Ljava/lang/String;)V // method@0010
-000494: 1c02 0a00 |0034: const-class v2, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
-000498: 0800 1500 |0036: move-object/from16 v0, v21
-00049c: 7120 0f00 0200 |0038: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000f
-0004a2: 1702 15cd 5b07 |003b: const-wide/32 v2, #float 1.6536e-34 // #075bcd15
-0004a8: 0500 1600 |003e: move-wide/from16 v0, v22
-0004ac: 7140 0e00 3210 |0040: invoke-static {v2, v3, v0, v1}, Ljunit/framework/Assert;.assertEquals:(JJ)V // method@000e
-0004b2: 7100 0900 0000 |0043: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0009
-0004b8: 0c02 |0046: move-result-object v2
-0004ba: 1c03 0a00 |0047: const-class v3, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a
-0004be: 6e40 0800 32ba |0049: invoke-virtual {v2, v3, v10, v11}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0008
-0004c4: 0c02 |004c: move-result-object v2
-0004c6: 2203 1400 |004d: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0014
-0004ca: 7020 0700 2300 |004f: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0007
-0004d0: 1103 |0052: return-object v3
+ registers : 8
+ ins : 2
+ outs : 2
+ insns size : 80 16-bit code units
+000d88: |[000d88] invokecustom.InvokeCustom.checkStaticFieldTest9:(Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V
+000d98: 1405 1032 5476 |0000: const v5, #float 1.07596e+33 // #76543210
+000d9e: fa10 4000 0600 0200 |0003: invoke-polymorphic {v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, ()I // method@0040, proto@0002
+000da6: 0a00 |0007: move-result v0
+000da8: fa20 4000 5700 2500 |0008: invoke-polymorphic {v7, v5}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, (I)V // method@0040, proto@0025
+000db0: fa10 4000 0600 0200 |000c: invoke-polymorphic {v6}, Ljava/lang/invoke/MethodHandle;.invokeExact:([Ljava/lang/Object;)Ljava/lang/Object;, ()I // method@0040, proto@0002
+000db8: 0a01 |0010: move-result v1
+000dba: 6202 0200 |0011: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000dbe: 2203 1000 |0013: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+000dc2: 7010 3000 0300 |0015: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+000dc8: 1a04 6a00 |0018: const-string v4, "checkStaticFieldTest9: old " // string@006a
+000dcc: 6e20 3600 4300 |001a: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000dd2: 0c03 |001d: move-result-object v3
+000dd4: 6e20 3300 0300 |001e: invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+000dda: 0c00 |0021: move-result-object v0
+000ddc: 1a03 0700 |0022: const-string v3, " new " // string@0007
+000de0: 6e20 3600 3000 |0024: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000de6: 0c00 |0027: move-result-object v0
+000de8: 6e20 3300 1000 |0028: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+000dee: 0c00 |002b: move-result-object v0
+000df0: 1a03 0600 |002c: const-string v3, " expected " // string@0006
+000df4: 6e20 3600 3000 |002e: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000dfa: 0c00 |0031: move-result-object v0
+000dfc: 6e20 3300 5000 |0032: invoke-virtual {v0, v5}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+000e02: 0c00 |0035: move-result-object v0
+000e04: 1a03 0100 |0036: const-string v3, " " // string@0001
+000e08: 6e20 3600 3000 |0038: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+000e0e: 0c00 |003b: move-result-object v0
+000e10: 6e10 3700 0000 |003c: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+000e16: 0c00 |003f: move-result-object v0
+000e18: 6e20 2300 0200 |0040: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.print:(Ljava/lang/String;)V // method@0023
+000e1e: 6202 0200 |0043: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000e22: 3351 0800 |0045: if-ne v1, v5, 004d // +0008
+000e26: 1a00 4400 |0047: const-string v0, "OK" // string@0044
+000e2a: 6e20 2900 0200 |0049: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000e30: 0e00 |004c: return-void
+000e32: 1a00 1100 |004d: const-string v0, "ERROR" // string@0011
+000e36: 28fa |004f: goto 0049 // -0006
catches : (none)
positions :
- 0x0000 line=62
- 0x0003 line=63
- 0x0007 line=64
- 0x000c line=65
- 0x0011 line=66
- 0x0017 line=67
- 0x0020 line=68
- 0x002c line=69
- 0x0034 line=70
- 0x003b line=71
- 0x0043 line=72
- 0x004d line=73
- locals :
- 0x0000 - 0x0053 reg=9 (null) Ljava/lang/invoke/MethodHandles$Lookup;
- 0x0000 - 0x0053 reg=10 (null) Ljava/lang/String;
- 0x0000 - 0x0053 reg=11 (null) Ljava/lang/invoke/MethodType;
- 0x0000 - 0x0053 reg=12 (null) Z
- 0x0000 - 0x0053 reg=13 (null) B
- 0x0000 - 0x0053 reg=14 (null) C
- 0x0000 - 0x0053 reg=15 (null) S
- 0x0000 - 0x0053 reg=16 (null) I
- 0x0000 - 0x0053 reg=17 (null) F
- 0x0000 - 0x0053 reg=18 (null) D
- 0x0000 - 0x0053 reg=20 (null) Ljava/lang/String;
- 0x0000 - 0x0053 reg=21 (null) Ljava/lang/Class;
- 0x0000 - 0x0053 reg=22 (null) J
-
- #3 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
+ 0x0003 line=107
+ 0x0008 line=108
+ 0x000c line=109
+ 0x0011 line=110
+ 0x0043 line=112
+ 0x004c line=113
+ 0x004d line=112
+ locals :
+ 0x0000 - 0x0050 reg=6 (null) Ljava/lang/invoke/MethodHandle;
+ 0x0000 - 0x0050 reg=7 (null) Ljava/lang/invoke/MethodHandle;
+
+ #9 : (in Linvokecustom/InvokeCustom;)
+ name : 'lambda$lambdaTest$0'
+ type : '(Ljava/lang/String;)Z'
+ access : 0x100a (PRIVATE STATIC SYNTHETIC)
+ code -
+ registers : 3
+ ins : 1
+ outs : 2
+ insns size : 11 16-bit code units
+000e38: |[000e38] invokecustom.InvokeCustom.lambda$lambdaTest$0:(Ljava/lang/String;)Z
+000e48: 1a00 4500 |0000: const-string v0, "One" // string@0045
+000e4c: 6e10 2f00 0200 |0002: invoke-virtual {v2}, Ljava/lang/String;.trim:()Ljava/lang/String; // method@002f
+000e52: 0c01 |0005: move-result-object v1
+000e54: 6e20 2e00 1000 |0006: invoke-virtual {v0, v1}, Ljava/lang/String;.equals:(Ljava/lang/Object;)Z // method@002e
+000e5a: 0a00 |0009: move-result v0
+000e5c: 0f00 |000a: return v0
+ catches : (none)
+ positions :
+ 0x0000 line=192
+ locals :
+ 0x0000 - 0x000b reg=2 (null) Ljava/lang/String;
+
+ #10 : (in Linvokecustom/InvokeCustom;)
+ name : 'lambdaTest'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 3
+ ins : 0
+ outs : 2
+ insns size : 71 16-bit code units
+000e60: |[000e60] invokecustom.InvokeCustom.lambdaTest:()V
+000e70: 1230 |0000: const/4 v0, #int 3 // #3
+000e72: 2300 2500 |0001: new-array v0, v0, [Ljava/lang/String; // type@0025
+000e76: 1201 |0003: const/4 v1, #int 0 // #0
+000e78: 1a02 4900 |0004: const-string v2, "Three" // string@0049
+000e7c: 4d02 0001 |0006: aput-object v2, v0, v1
+000e80: 1211 |0008: const/4 v1, #int 1 // #1
+000e82: 1a02 4500 |0009: const-string v2, "One" // string@0045
+000e86: 4d02 0001 |000b: aput-object v2, v0, v1
+000e8a: 1221 |000d: const/4 v1, #int 2 // #2
+000e8c: 1a02 1600 |000e: const-string v2, "FortyTwo" // string@0016
+000e90: 4d02 0001 |0010: aput-object v2, v0, v1
+000e94: 7110 4700 0000 |0012: invoke-static {v0}, Ljava/util/Arrays;.asList:([Ljava/lang/Object;)Ljava/util/List; // method@0047
+000e9a: 0c01 |0015: move-result-object v1
+000e9c: 7210 4800 0100 |0016: invoke-interface {v1}, Ljava/util/List;.stream:()Ljava/util/stream/Stream; // method@0048
+000ea2: 0c00 |0019: move-result-object v0
+000ea4: fc00 0000 0000 |001a: invoke-custom {}, call_site@0000
+000eaa: 0c02 |001d: move-result-object v2
+000eac: 7220 4a00 2000 |001e: invoke-interface {v0, v2}, Ljava/util/stream/Stream;.filter:(Ljava/util/function/Predicate;)Ljava/util/stream/Stream; // method@004a
+000eb2: 0c00 |0021: move-result-object v0
+000eb4: fc00 0100 0000 |0022: invoke-custom {}, call_site@0001
+000eba: 0c02 |0025: move-result-object v2
+000ebc: 7220 4d00 2000 |0026: invoke-interface {v0, v2}, Ljava/util/stream/Stream;.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream; // method@004d
+000ec2: 0c00 |0029: move-result-object v0
+000ec4: 7210 4b00 0000 |002a: invoke-interface {v0}, Ljava/util/stream/Stream;.findAny:()Ljava/util/Optional; // method@004b
+000eca: 0c00 |002d: move-result-object v0
+000ecc: 1a02 0000 |002e: const-string v2, "" // string@0000
+000ed0: 6e20 4900 2000 |0030: invoke-virtual {v0, v2}, Ljava/util/Optional;.orElse:(Ljava/lang/Object;)Ljava/lang/Object; // method@0049
+000ed6: 0c00 |0033: move-result-object v0
+000ed8: 1f00 0f00 |0034: check-cast v0, Ljava/lang/String; // type@000f
+000edc: 7210 4800 0100 |0036: invoke-interface {v1}, Ljava/util/List;.stream:()Ljava/util/stream/Stream; // method@0048
+000ee2: 0c00 |0039: move-result-object v0
+000ee4: 6201 0200 |003a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000ee8: 6e10 2c00 0100 |003c: invoke-virtual {v1}, Ljava/lang/Object;.getClass:()Ljava/lang/Class; // method@002c
+000eee: fc10 0200 0100 |003f: invoke-custom {v1}, call_site@0002
+000ef4: 0c01 |0042: move-result-object v1
+000ef6: 7220 4c00 1000 |0043: invoke-interface {v0, v1}, Ljava/util/stream/Stream;.forEach:(Ljava/util/function/Consumer;)V // method@004c
+000efc: 0e00 |0046: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=191
+ 0x0016 line=192
+ 0x0026 line=193
+ 0x0036 line=194
+ 0x0046 line=195
+ locals :
+
+ #11 : (in Linvokecustom/InvokeCustom;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
+ registers : 1
+ ins : 1
+ outs : 0
+ insns size : 28 16-bit code units
+000f00: |[000f00] invokecustom.InvokeCustom.main:([Ljava/lang/String;)V
+000f10: 7100 1700 0000 |0000: invoke-static {}, Linvokecustom/InvokeCustom;.test1:()V // method@0017
+000f16: 7100 1800 0000 |0003: invoke-static {}, Linvokecustom/InvokeCustom;.test2:()V // method@0018
+000f1c: 7100 1900 0000 |0006: invoke-static {}, Linvokecustom/InvokeCustom;.test3:()V // method@0019
+000f22: 7100 1a00 0000 |0009: invoke-static {}, Linvokecustom/InvokeCustom;.test4:()V // method@001a
+000f28: 7100 1b00 0000 |000c: invoke-static {}, Linvokecustom/InvokeCustom;.test5:()V // method@001b
+000f2e: 7100 1c00 0000 |000f: invoke-static {}, Linvokecustom/InvokeCustom;.test6:()V // method@001c
+000f34: 7100 1d00 0000 |0012: invoke-static {}, Linvokecustom/InvokeCustom;.test7:()V // method@001d
+000f3a: 7100 1e00 0000 |0015: invoke-static {}, Linvokecustom/InvokeCustom;.test8:()V // method@001e
+000f40: 7100 1f00 0000 |0018: invoke-static {}, Linvokecustom/InvokeCustom;.test9:()V // method@001f
+000f46: 0e00 |001b: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #12 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest1'
+ type : '()V'
+ access : 0x000a (PRIVATE STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 2
+ insns size : 8 16-bit code units
+000f48: |[000f48] invokecustom.InvokeCustom.targetMethodTest1:()V
+000f58: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000f5c: 1a01 1700 |0002: const-string v1, "Hello World!" // string@0017
+000f60: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000f66: 0e00 |0007: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=45
+ 0x0007 line=46
+ locals :
+
+ #13 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest2'
+ type : '(ZBCSIFJDLjava/lang/String;)V'
+ access : 0x000a (PRIVATE STATIC)
+ code -
+ registers : 13
+ ins : 11
+ outs : 3
+ insns size : 46 16-bit code units
+000f68: |[000f68] invokecustom.InvokeCustom.targetMethodTest2:(ZBCSIFJDLjava/lang/String;)V
+000f78: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000f7c: 6e20 2a00 2000 |0002: invoke-virtual {v0, v2}, Ljava/io/PrintStream;.println:(Z)V // method@002a
+000f82: 6200 0200 |0005: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000f86: 6e20 2700 3000 |0007: invoke-virtual {v0, v3}, Ljava/io/PrintStream;.println:(I)V // method@0027
+000f8c: 6200 0200 |000a: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000f90: 6e20 2400 4000 |000c: invoke-virtual {v0, v4}, Ljava/io/PrintStream;.println:(C)V // method@0024
+000f96: 6200 0200 |000f: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000f9a: 6e20 2700 5000 |0011: invoke-virtual {v0, v5}, Ljava/io/PrintStream;.println:(I)V // method@0027
+000fa0: 6200 0200 |0014: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000fa4: 6e20 2700 6000 |0016: invoke-virtual {v0, v6}, Ljava/io/PrintStream;.println:(I)V // method@0027
+000faa: 6200 0200 |0019: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000fae: 6e20 2600 7000 |001b: invoke-virtual {v0, v7}, Ljava/io/PrintStream;.println:(F)V // method@0026
+000fb4: 6200 0200 |001e: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000fb8: 6e30 2800 8009 |0020: invoke-virtual {v0, v8, v9}, Ljava/io/PrintStream;.println:(J)V // method@0028
+000fbe: 6200 0200 |0023: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000fc2: 6e30 2500 a00b |0025: invoke-virtual {v0, v10, v11}, Ljava/io/PrintStream;.println:(D)V // method@0025
+000fc8: 6200 0200 |0028: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000fcc: 6e20 2900 c000 |002a: invoke-virtual {v0, v12}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000fd2: 0e00 |002d: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=50
+ 0x0005 line=51
+ 0x000a line=52
+ 0x000f line=53
+ 0x0014 line=54
+ 0x0019 line=55
+ 0x001e line=56
+ 0x0023 line=57
+ 0x0028 line=58
+ 0x002d line=59
+ locals :
+ 0x0000 - 0x002e reg=2 (null) Z
+ 0x0000 - 0x002e reg=3 (null) B
+ 0x0000 - 0x002e reg=4 (null) C
+ 0x0000 - 0x002e reg=5 (null) S
+ 0x0000 - 0x002e reg=6 (null) I
+ 0x0000 - 0x002e reg=7 (null) F
+ 0x0000 - 0x002e reg=8 (null) J
+ 0x0000 - 0x002e reg=10 (null) D
+ 0x0000 - 0x002e reg=12 (null) Ljava/lang/String;
+
+ #14 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest3'
+ type : '()V'
+ access : 0x000a (PRIVATE STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 2
+ insns size : 8 16-bit code units
+000fd4: |[000fd4] invokecustom.InvokeCustom.targetMethodTest3:()V
+000fe4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+000fe8: 1a01 8800 |0002: const-string v1, "targetMethodTest3 from InvokeCustom" // string@0088
+000fec: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+000ff2: 0e00 |0007: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=62
+ 0x0007 line=63
+ locals :
+
+ #15 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest5'
+ type : '(III)I'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 7
+ ins : 3
+ outs : 2
+ insns size : 83 16-bit code units
+000ff4: |[000ff4] invokecustom.InvokeCustom.targetMethodTest5:(III)I
+001004: 9000 0405 |0000: add-int v0, v4, v5
+001008: 6201 0200 |0002: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+00100c: 2202 1000 |0004: new-instance v2, Ljava/lang/StringBuilder; // type@0010
+001010: 7010 3000 0200 |0006: invoke-direct {v2}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+001016: 1a03 8d00 |0009: const-string v3, "targetMethodTest5 " // string@008d
+00101a: 6e20 3600 3200 |000b: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001020: 0c02 |000e: move-result-object v2
+001022: 6e20 3300 4200 |000f: invoke-virtual {v2, v4}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+001028: 0c02 |0012: move-result-object v2
+00102a: 1a03 0400 |0013: const-string v3, " + " // string@0004
+00102e: 6e20 3600 3200 |0015: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001034: 0c02 |0018: move-result-object v2
+001036: 6e20 3300 5200 |0019: invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+00103c: 0c02 |001c: move-result-object v2
+00103e: 1a03 0500 |001d: const-string v3, " = " // string@0005
+001042: 6e20 3600 3200 |001f: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001048: 0c02 |0022: move-result-object v2
+00104a: 6e20 3300 0200 |0023: invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+001050: 0c02 |0026: move-result-object v2
+001052: 6e10 3700 0200 |0027: invoke-virtual {v2}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+001058: 0c02 |002a: move-result-object v2
+00105a: 6e20 2900 2100 |002b: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001060: 3260 2400 |002e: if-eq v0, v6, 0052 // +0024
+001064: 6201 0200 |0030: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+001068: 2202 1000 |0032: new-instance v2, Ljava/lang/StringBuilder; // type@0010
+00106c: 7010 3000 0200 |0034: invoke-direct {v2}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+001072: 1a03 1400 |0037: const-string v3, "Failed " // string@0014
+001076: 6e20 3600 3200 |0039: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+00107c: 0c02 |003c: move-result-object v2
+00107e: 6e20 3300 0200 |003d: invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+001084: 0c02 |0040: move-result-object v2
+001086: 1a03 0200 |0041: const-string v3, " != " // string@0002
+00108a: 6e20 3600 3200 |0043: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001090: 0c02 |0046: move-result-object v2
+001092: 6e20 3300 6200 |0047: invoke-virtual {v2, v6}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+001098: 0c02 |004a: move-result-object v2
+00109a: 6e10 3700 0200 |004b: invoke-virtual {v2}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+0010a0: 0c02 |004e: move-result-object v2
+0010a2: 6e20 2900 2100 |004f: invoke-virtual {v1, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+0010a8: 0f00 |0052: return v0
+ catches : (none)
+ positions :
+ 0x0000 line=72
+ 0x0002 line=73
+ 0x002e line=74
+ 0x0030 line=75
+ 0x0052 line=77
+ locals :
+ 0x0000 - 0x0053 reg=4 (null) I
+ 0x0000 - 0x0053 reg=5 (null) I
+ 0x0000 - 0x0053 reg=6 (null) I
+
+ #16 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest6'
+ type : '(JJJ)J'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 12
+ ins : 6
+ outs : 3
+ insns size : 85 16-bit code units
+0010ac: |[0010ac] invokecustom.InvokeCustom.targetMethodTest6:(JJJ)J
+0010bc: 9b00 0608 |0000: add-long v0, v6, v8
+0010c0: 6202 0200 |0002: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0010c4: 2203 1000 |0004: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+0010c8: 7010 3000 0300 |0006: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+0010ce: 1a04 9000 |0009: const-string v4, "targetMethodTest6 " // string@0090
+0010d2: 6e20 3600 4300 |000b: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0010d8: 0c03 |000e: move-result-object v3
+0010da: 6e30 3400 6307 |000f: invoke-virtual {v3, v6, v7}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+0010e0: 0c03 |0012: move-result-object v3
+0010e2: 1a04 0400 |0013: const-string v4, " + " // string@0004
+0010e6: 6e20 3600 4300 |0015: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0010ec: 0c03 |0018: move-result-object v3
+0010ee: 6e30 3400 8309 |0019: invoke-virtual {v3, v8, v9}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+0010f4: 0c03 |001c: move-result-object v3
+0010f6: 1a04 0500 |001d: const-string v4, " = " // string@0005
+0010fa: 6e20 3600 4300 |001f: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001100: 0c03 |0022: move-result-object v3
+001102: 6e30 3400 0301 |0023: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+001108: 0c03 |0026: move-result-object v3
+00110a: 6e10 3700 0300 |0027: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+001110: 0c03 |002a: move-result-object v3
+001112: 6e20 2900 3200 |002b: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001118: 3102 000a |002e: cmp-long v2, v0, v10
+00111c: 3802 2400 |0030: if-eqz v2, 0054 // +0024
+001120: 6202 0200 |0032: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+001124: 2203 1000 |0034: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+001128: 7010 3000 0300 |0036: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+00112e: 1a04 1400 |0039: const-string v4, "Failed " // string@0014
+001132: 6e20 3600 4300 |003b: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001138: 0c03 |003e: move-result-object v3
+00113a: 6e30 3400 0301 |003f: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+001140: 0c03 |0042: move-result-object v3
+001142: 1a04 0200 |0043: const-string v4, " != " // string@0002
+001146: 6e20 3600 4300 |0045: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+00114c: 0c03 |0048: move-result-object v3
+00114e: 6e30 3400 a30b |0049: invoke-virtual {v3, v10, v11}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+001154: 0c03 |004c: move-result-object v3
+001156: 6e10 3700 0300 |004d: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+00115c: 0c03 |0050: move-result-object v3
+00115e: 6e20 2900 3200 |0051: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001164: 1000 |0054: return-wide v0
+ catches : (none)
+ positions :
+ 0x0000 line=81
+ 0x0002 line=82
+ 0x002e line=83
+ 0x0032 line=84
+ 0x0054 line=86
+ locals :
+ 0x0000 - 0x0055 reg=6 (null) J
+ 0x0000 - 0x0055 reg=8 (null) J
+ 0x0000 - 0x0055 reg=10 (null) J
+
+ #17 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest7'
+ type : '(FFD)D'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 10
+ ins : 4
+ outs : 3
+ insns size : 86 16-bit code units
+001168: |[001168] invokecustom.InvokeCustom.targetMethodTest7:(FFD)D
+001178: a800 0607 |0000: mul-float v0, v6, v7
+00117c: 8900 |0002: float-to-double v0, v0
+00117e: 6202 0200 |0003: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+001182: 2203 1000 |0005: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+001186: 7010 3000 0300 |0007: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+00118c: 1a04 9300 |000a: const-string v4, "targetMethodTest7 " // string@0093
+001190: 6e20 3600 4300 |000c: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001196: 0c03 |000f: move-result-object v3
+001198: 6e20 3200 6300 |0010: invoke-virtual {v3, v6}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032
+00119e: 0c03 |0013: move-result-object v3
+0011a0: 1a04 0300 |0014: const-string v4, " * " // string@0003
+0011a4: 6e20 3600 4300 |0016: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0011aa: 0c03 |0019: move-result-object v3
+0011ac: 6e20 3200 7300 |001a: invoke-virtual {v3, v7}, Ljava/lang/StringBuilder;.append:(F)Ljava/lang/StringBuilder; // method@0032
+0011b2: 0c03 |001d: move-result-object v3
+0011b4: 1a04 0500 |001e: const-string v4, " = " // string@0005
+0011b8: 6e20 3600 4300 |0020: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0011be: 0c03 |0023: move-result-object v3
+0011c0: 6e30 3100 0301 |0024: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031
+0011c6: 0c03 |0027: move-result-object v3
+0011c8: 6e10 3700 0300 |0028: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+0011ce: 0c03 |002b: move-result-object v3
+0011d0: 6e20 2900 3200 |002c: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+0011d6: 2f02 0008 |002f: cmpl-double v2, v0, v8
+0011da: 3802 2400 |0031: if-eqz v2, 0055 // +0024
+0011de: 6202 0200 |0033: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0011e2: 2203 1000 |0035: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+0011e6: 7010 3000 0300 |0037: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+0011ec: 1a04 1400 |003a: const-string v4, "Failed " // string@0014
+0011f0: 6e20 3600 4300 |003c: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0011f6: 0c03 |003f: move-result-object v3
+0011f8: 6e30 3100 0301 |0040: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031
+0011fe: 0c03 |0043: move-result-object v3
+001200: 1a04 0200 |0044: const-string v4, " != " // string@0002
+001204: 6e20 3600 4300 |0046: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+00120a: 0c03 |0049: move-result-object v3
+00120c: 6e30 3100 8309 |004a: invoke-virtual {v3, v8, v9}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031
+001212: 0c03 |004d: move-result-object v3
+001214: 6e10 3700 0300 |004e: invoke-virtual {v3}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+00121a: 0c03 |0051: move-result-object v3
+00121c: 6e20 2900 3200 |0052: invoke-virtual {v2, v3}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001222: 1000 |0055: return-wide v0
+ catches : (none)
+ positions :
+ 0x0000 line=90
+ 0x0003 line=91
+ 0x002f line=92
+ 0x0033 line=93
+ 0x0055 line=95
+ locals :
+ 0x0000 - 0x0056 reg=6 (null) F
+ 0x0000 - 0x0056 reg=7 (null) F
+ 0x0000 - 0x0056 reg=8 (null) D
+
+ #18 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest8'
+ type : '(Ljava/lang/String;)V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
registers : 4
ins : 1
outs : 2
- insns size : 12 16-bit code units
-0004d4: |[0004d4] com.android.jack.java7.invokecustom.test004.Tests.main:([Ljava/lang/String;)V
-0004e4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
-0004e8: 1221 |0002: const/4 v1, #int 2 // #2
-0004ea: 1232 |0003: const/4 v2, #int 3 // #3
-0004ec: fc20 0000 2100 |0004: invoke-custom {v1, v2}, call_site@0000
-0004f2: 0a01 |0007: move-result v1
-0004f4: 6e20 0500 1000 |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(I)V // method@0005
-0004fa: 0e00 |000b: return-void
+ insns size : 25 16-bit code units
+001224: |[001224] invokecustom.InvokeCustom.targetMethodTest8:(Ljava/lang/String;)V
+001234: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+001238: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+00123c: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+001242: 1a02 9500 |0007: const-string v2, "targetMethodTest8 " // string@0095
+001246: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+00124c: 0c01 |000c: move-result-object v1
+00124e: 6e20 3600 3100 |000d: invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001254: 0c01 |0010: move-result-object v1
+001256: 6e10 3700 0100 |0011: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+00125c: 0c01 |0014: move-result-object v1
+00125e: 6e20 2900 1000 |0015: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001264: 0e00 |0018: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=99
+ 0x0018 line=100
+ locals :
+ 0x0000 - 0x0019 reg=3 (null) Ljava/lang/String;
+
+ #19 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest9'
+ type : '()V'
+ access : 0x000a (PRIVATE STATIC)
+ code -
+ registers : 2
+ ins : 0
+ outs : 2
+ insns size : 8 16-bit code units
+001268: |[001268] invokecustom.InvokeCustom.targetMethodTest9:()V
+001278: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+00127c: 1a01 9700 |0002: const-string v1, "targetMethodTest9()" // string@0097
+001280: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001286: 0e00 |0007: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=133
+ 0x0007 line=134
+ locals :
+
+ #20 : (in Linvokecustom/InvokeCustom;)
+ name : 'test1'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 0
+ ins : 0
+ outs : 0
+ insns size : 4 16-bit code units
+001288: |[001288] invokecustom.InvokeCustom.test1:()V
+001298: fc00 0300 0000 |0000: invoke-custom {}, call_site@0003
+00129e: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #21 : (in Linvokecustom/InvokeCustom;)
+ name : 'test2'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 11
+ ins : 0
+ outs : 11
+ insns size : 27 16-bit code units
+0012a0: |[0012a0] invokecustom.InvokeCustom.test2:()V
+0012b0: 1210 |0000: const/4 v0, #int 1 // #1
+0012b2: 1301 7f00 |0001: const/16 v1, #int 127 // #7f
+0012b6: 1302 6300 |0003: const/16 v2, #int 99 // #63
+0012ba: 1303 0004 |0005: const/16 v3, #int 1024 // #400
+0012be: 1404 40e2 0100 |0007: const v4, #float 1.72999e-40 // #0001e240
+0012c4: 1405 9a99 993f |000a: const v5, #float 1.2 // #3f99999a
+0012ca: 1706 15cd 5b07 |000d: const-wide/32 v6, #float 1.6536e-34 // #075bcd15
+0012d0: 1808 b6fa f8b0 4819 0c40 |0010: const-wide v8, #double 3.51235 // #400c1948b0f8fab6
+0012da: 1a0a 4800 |0015: const-string v10, "String" // string@0048
+0012de: fd0b 0400 0000 |0017: invoke-custom/range {v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10}, call_site@0004
+0012e4: 0e00 |001a: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #22 : (in Linvokecustom/InvokeCustom;)
+ name : 'test3'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 0
+ ins : 0
+ outs : 0
+ insns size : 4 16-bit code units
+0012e8: |[0012e8] invokecustom.InvokeCustom.test3:()V
+0012f8: fc00 0b00 0000 |0000: invoke-custom {}, call_site@000b
+0012fe: 0e00 |0003: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #23 : (in Linvokecustom/InvokeCustom;)
+ name : 'test4'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 1
+ insns size : 9 16-bit code units
+001300: |[001300] invokecustom.InvokeCustom.test4:()V
+001310: 2200 0700 |0000: new-instance v0, Linvokecustom/InvokeCustom; // type@0007
+001314: 7010 0100 0000 |0002: invoke-direct {v0}, Linvokecustom/InvokeCustom;.<init>:()V // method@0001
+00131a: fc10 0c00 0000 |0005: invoke-custom {v0}, call_site@000c
+001320: 0e00 |0008: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #24 : (in Linvokecustom/InvokeCustom;)
+ name : 'test5'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 4
+ ins : 0
+ outs : 3
+ insns size : 35 16-bit code units
+001324: |[001324] invokecustom.InvokeCustom.test5:()V
+001334: 1300 e803 |0000: const/16 v0, #int 1000 // #3e8
+001338: 1301 65fc |0002: const/16 v1, #int -923 // #fc65
+00133c: 1302 4d00 |0004: const/16 v2, #int 77 // #4d
+001340: fc30 0500 1002 |0006: invoke-custom {v0, v1, v2}, call_site@0005
+001346: 0a00 |0009: move-result v0
+001348: 6201 0200 |000a: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+00134c: 2202 1000 |000c: new-instance v2, Ljava/lang/StringBuilder; // type@0010
+001350: 7010 3000 0200 |000e: invoke-direct {v2}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+001356: 1a03 8e00 |0011: const-string v3, "targetMethodTest5 returned: " // string@008e
+00135a: 6e20 3600 3200 |0013: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+001360: 0c02 |0016: move-result-object v2
+001362: 6e20 3300 0200 |0017: invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;.append:(I)Ljava/lang/StringBuilder; // method@0033
+001368: 0c00 |001a: move-result-object v0
+00136a: 6e10 3700 0000 |001b: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+001370: 0c00 |001e: move-result-object v0
+001372: 6e20 2900 0100 |001f: invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001378: 0e00 |0022: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #25 : (in Linvokecustom/InvokeCustom;)
+ name : 'test6'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 6
+ ins : 0
+ outs : 6
+ insns size : 44 16-bit code units
+00137c: |[00137c] invokecustom.InvokeCustom.test6:()V
+00138c: 1800 7777 7777 7707 0000 |0000: const-wide v0, #double 4.05612e-311 // #0000077777777777
+001396: 1802 efee eeee eefe ffff |0005: const-wide v2, #double -nan // #fffffeeeeeeeeeef
+0013a0: 1804 6666 6666 6606 0000 |000a: const-wide v4, #double 3.47668e-311 // #0000066666666666
+0013aa: fd06 0600 0000 |000f: invoke-custom/range {v0, v1, v2, v3, v4, v5}, call_site@0006
+0013b0: 0b00 |0012: move-result-wide v0
+0013b2: 6202 0200 |0013: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0013b6: 2203 1000 |0015: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+0013ba: 7010 3000 0300 |0017: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+0013c0: 1a04 9100 |001a: const-string v4, "targetMethodTest6 returned: " // string@0091
+0013c4: 6e20 3600 4300 |001c: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0013ca: 0c03 |001f: move-result-object v3
+0013cc: 6e30 3400 0301 |0020: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(J)Ljava/lang/StringBuilder; // method@0034
+0013d2: 0c00 |0023: move-result-object v0
+0013d4: 6e10 3700 0000 |0024: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+0013da: 0c00 |0027: move-result-object v0
+0013dc: 6e20 2900 0200 |0028: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+0013e2: 0e00 |002b: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #26 : (in Linvokecustom/InvokeCustom;)
+ name : 'test7'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 5
+ ins : 0
+ outs : 4
+ insns size : 40 16-bit code units
+0013e4: |[0013e4] invokecustom.InvokeCustom.test7:()V
+0013f4: 1400 0040 003f |0000: const v0, #float 0.500977 // #3f004000
+0013fa: 1401 0040 00bf |0003: const v1, #float -0.500977 // #bf004000
+001400: 1802 0000 0000 0410 d0bf |0006: const-wide v2, #double -0.250978 // #bfd0100400000000
+00140a: fc40 0700 1032 |000b: invoke-custom {v0, v1, v2, v3}, call_site@0007
+001410: 0b00 |000e: move-result-wide v0
+001412: 6202 0200 |000f: sget-object v2, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+001416: 2203 1000 |0011: new-instance v3, Ljava/lang/StringBuilder; // type@0010
+00141a: 7010 3000 0300 |0013: invoke-direct {v3}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+001420: 1a04 9100 |0016: const-string v4, "targetMethodTest6 returned: " // string@0091
+001424: 6e20 3600 4300 |0018: invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+00142a: 0c03 |001b: move-result-object v3
+00142c: 6e30 3100 0301 |001c: invoke-virtual {v3, v0, v1}, Ljava/lang/StringBuilder;.append:(D)Ljava/lang/StringBuilder; // method@0031
+001432: 0c00 |001f: move-result-object v0
+001434: 6e10 3700 0000 |0020: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+00143a: 0c00 |0023: move-result-object v0
+00143c: 6e20 2900 0200 |0024: invoke-virtual {v2, v0}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001442: 0e00 |0027: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #27 : (in Linvokecustom/InvokeCustom;)
+ name : 'test8'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 1
+ ins : 0
+ outs : 1
+ insns size : 16 16-bit code units
+001444: |[001444] invokecustom.InvokeCustom.test8:()V
+001454: 1a00 1500 |0000: const-string v0, "First invokedynamic invocation" // string@0015
+001458: fc10 0800 0000 |0002: invoke-custom {v0}, call_site@0008
+00145e: 1a00 4700 |0005: const-string v0, "Second invokedynamic invocation" // string@0047
+001462: fc10 0900 0000 |0007: invoke-custom {v0}, call_site@0009
+001468: 1a00 1000 |000a: const-string v0, "Dupe first invokedynamic invocation" // string@0010
+00146c: fc10 0a00 0000 |000c: invoke-custom {v0}, call_site@000a
+001472: 0e00 |000f: return-void
+ catches : (none)
+ positions :
+ locals :
+
+ #28 : (in Linvokecustom/InvokeCustom;)
+ name : 'test9'
+ type : '()V'
+ access : 0x0009 (PUBLIC STATIC)
+ code -
+ registers : 0
+ ins : 0
+ outs : 0
+ insns size : 4 16-bit code units
+001474: |[001474] invokecustom.InvokeCustom.test9:()V
+001484: fc00 0d00 0000 |0000: invoke-custom {}, call_site@000d
+00148a: 0e00 |0003: return-void
catches : (none)
positions :
- 0x0000 line=82
- 0x000b line=83
locals :
- 0x0000 - 0x000c reg=3 (null) [Ljava/lang/String;
Virtual methods -
- #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;)
- name : 'test'
+ #0 : (in Linvokecustom/InvokeCustom;)
+ name : 'helperMethodTest9'
+ type : '()V'
+ access : 0x0001 (PUBLIC)
+ code -
+ registers : 4
+ ins : 1
+ outs : 2
+ insns size : 27 16-bit code units
+00148c: |[00148c] invokecustom.InvokeCustom.helperMethodTest9:()V
+00149c: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0014a0: 2201 1000 |0002: new-instance v1, Ljava/lang/StringBuilder; // type@0010
+0014a4: 7010 3000 0100 |0004: invoke-direct {v1}, Ljava/lang/StringBuilder;.<init>:()V // method@0030
+0014aa: 1a02 7300 |0007: const-string v2, "helperMethodTest9 in " // string@0073
+0014ae: 6e20 3600 2100 |0009: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0036
+0014b4: 0c01 |000c: move-result-object v1
+0014b6: 1c02 0700 |000d: const-class v2, Linvokecustom/InvokeCustom; // type@0007
+0014ba: 6e20 3500 2100 |000f: invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; // method@0035
+0014c0: 0c01 |0012: move-result-object v1
+0014c2: 6e10 3700 0100 |0013: invoke-virtual {v1}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0037
+0014c8: 0c01 |0016: move-result-object v1
+0014ca: 6e20 2900 1000 |0017: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+0014d0: 0e00 |001a: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=129
+ 0x001a line=130
+ locals :
+ 0x0000 - 0x001b reg=3 this Linvokecustom/InvokeCustom;
+
+ #1 : (in Linvokecustom/InvokeCustom;)
+ name : 'run'
type : '()V'
access : 0x0001 (PUBLIC)
code -
registers : 3
ins : 1
outs : 2
- insns size : 11 16-bit code units
-0004fc: |[0004fc] com.android.jack.java7.invokecustom.test004.Tests.test:()V
-00050c: 1220 |0000: const/4 v0, #int 2 // #2
-00050e: 1231 |0001: const/4 v1, #int 3 // #3
-000510: fc20 0100 1000 |0002: invoke-custom {v0, v1}, call_site@0001
-000516: 0a00 |0005: move-result v0
-000518: 1251 |0006: const/4 v1, #int 5 // #5
-00051a: 7120 0d00 0100 |0007: invoke-static {v1, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d
-000520: 0e00 |000a: return-void
+ insns size : 8 16-bit code units
+0014d4: |[0014d4] invokecustom.InvokeCustom.run:()V
+0014e4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+0014e8: 1a01 8200 |0002: const-string v1, "run() for Test9" // string@0082
+0014ec: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+0014f2: 0e00 |0007: return-void
catches : (none)
positions :
- 0x0000 line=78
- 0x000a line=79
+ 0x0000 line=137
+ 0x0007 line=138
locals :
- 0x0000 - 0x000b reg=2 this Lcom/android/jack/java7/invokecustom/test004/Tests;
+ 0x0000 - 0x0008 reg=2 this Linvokecustom/InvokeCustom;
- source_file_idx : 38 (Tests.java)
+ #2 : (in Linvokecustom/InvokeCustom;)
+ name : 'targetMethodTest4'
+ type : '()V'
+ access : 0x0001 (PUBLIC)
+ code -
+ registers : 3
+ ins : 1
+ outs : 2
+ insns size : 8 16-bit code units
+0014f4: |[0014f4] invokecustom.InvokeCustom.targetMethodTest4:()V
+001504: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002
+001508: 1a01 8a00 |0002: const-string v1, "targetMethodTest4 from InvokeCustom (oops!)" // string@008a
+00150c: 6e20 2900 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0029
+001512: 0e00 |0007: return-void
+ catches : (none)
+ positions :
+ 0x0000 line=68
+ 0x0007 line=69
+ locals :
+ 0x0000 - 0x0008 reg=2 this Linvokecustom/InvokeCustom;
+
+ source_file_idx : 27 (InvokeCustom.java)
Method handle #0:
+ type : put-static
+ target : Linvokecustom/InvokeCustom; staticFieldTest9
+ target_type : I
+Method handle #1:
+ type : get-static
+ target : Linvokecustom/InvokeCustom; staticFieldTest9
+ target_type : I
+Method handle #2:
+ type : put-instance
+ target : Linvokecustom/InvokeCustom; fieldTest9
+ target_type : (Linvokecustom/InvokeCustom;
+Method handle #3:
+ type : get-instance
+ target : Linvokecustom/InvokeCustom; fieldTest9
+ target_type : (Linvokecustom/InvokeCustom;
+Method handle #4:
type : invoke-static
- target : Lcom/android/jack/java7/invokecustom/test004/Tests; linkerMethod
- target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;
-Call site #0:
- link_argument[0] : 0 (MethodHandle)
- link_argument[1] : add (String)
- link_argument[2] : (II)I (MethodType)
- link_argument[3] : 1 (int)
- link_argument[4] : 1 (int)
- link_argument[5] : 97 (int)
- link_argument[6] : 1024 (int)
- link_argument[7] : 1 (int)
- link_argument[8] : 11.1 (float)
- link_argument[9] : 2.2 (double)
- link_argument[10] : Hello (String)
- link_argument[11] : Tests (Class)
- link_argument[12] : 123456789 (long)
-Call site #1:
- link_argument[0] : 0 (MethodHandle)
- link_argument[1] : add (String)
- link_argument[2] : (II)I (MethodType)
+ target : Linvokecustom/InvokeCustom; bsmCreateCallSite
+ target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;
+Method handle #5:
+ type : invoke-static
+ target : Linvokecustom/InvokeCustom; bsmLookupStatic
+ target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+Method handle #6:
+ type : invoke-static
+ target : Linvokecustom/InvokeCustom; bsmLookupStaticWithExtraArgs
+ target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite;
+Method handle #7:
+ type : invoke-static
+ target : Linvokecustom/InvokeCustom; bsmLookupTest9
+ target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;
+Method handle #8:
+ type : invoke-static
+ target : Linvokecustom/InvokeCustom; lambda$lambdaTest$0
+ target_type : (Ljava/lang/String;)Z
+Method handle #9:
+ type : invoke-static
+ target : Ljava/lang/invoke/LambdaMetafactory; metafactory
+ target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+Method handle #10:
+ type : invoke-instance
+ target : Linvokecustom/InvokeCustom; helperMethodTest9
+ target_type : (Linvokecustom/InvokeCustom;)V
+Method handle #11:
+ type : invoke-instance
+ target : Ljava/io/PrintStream; println
+ target_type : (Ljava/io/PrintStream;Ljava/lang/String;)V
+Method handle #12:
+ type : invoke-instance
+ target : Ljava/lang/String; trim
+ target_type : (Ljava/lang/String;)Ljava/lang/String;
+Method handle #13:
+ type : invoke-constructor
+ target : Linvokecustom/InvokeCustom; <init>
+ target_type : (Linvokecustom/InvokeCustom;I)V
+Method handle #14:
+ type : invoke-direct
+ target : Linvokecustom/Super; targetMethodTest4
+ target_type : (Linvokecustom/Super;)V
+Method handle #15:
+ type : invoke-interface
+ target : Ljava/lang/Runnable; run
+ target_type : (Ljava/lang/Runnable;)V
+Call site #0: // offset 8450
+ link_argument[0] : 9 (MethodHandle)
+ link_argument[1] : test (String)
+ link_argument[2] : ()Ljava/util/function/Predicate; (MethodType)
+ link_argument[3] : (Ljava/lang/Object;)Z (MethodType)
+ link_argument[4] : 8 (MethodHandle)
+ link_argument[5] : (Ljava/lang/String;)Z (MethodType)
+Call site #1: // offset 8463
+ link_argument[0] : 9 (MethodHandle)
+ link_argument[1] : apply (String)
+ link_argument[2] : ()Ljava/util/function/Function; (MethodType)
+ link_argument[3] : (Ljava/lang/Object;)Ljava/lang/Object; (MethodType)
+ link_argument[4] : 12 (MethodHandle)
+ link_argument[5] : (Ljava/lang/String;)Ljava/lang/String; (MethodType)
+Call site #2: // offset 8476
+ link_argument[0] : 9 (MethodHandle)
+ link_argument[1] : accept (String)
+ link_argument[2] : (Ljava/io/PrintStream;)Ljava/util/function/Consumer; (MethodType)
+ link_argument[3] : (Ljava/lang/Object;)V (MethodType)
+ link_argument[4] : 11 (MethodHandle)
+ link_argument[5] : (Ljava/lang/String;)V (MethodType)
+Call site #3: // offset 8489
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest1 (String)
+ link_argument[2] : ()V (MethodType)
+Call site #4: // offset 8496
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest2 (String)
+ link_argument[2] : (ZBCSIFJDLjava/lang/String;)V (MethodType)
+Call site #5: // offset 8503
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest5 (String)
+ link_argument[2] : (III)I (MethodType)
+Call site #6: // offset 8510
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest6 (String)
+ link_argument[2] : (JJJ)J (MethodType)
+Call site #7: // offset 8517
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest7 (String)
+ link_argument[2] : (FFD)D (MethodType)
+Call site #8: // offset 8524
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest8 (String)
+ link_argument[2] : (Ljava/lang/String;)V (MethodType)
+Call site #9: // offset 8524
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest8 (String)
+ link_argument[2] : (Ljava/lang/String;)V (MethodType)
+Call site #10: // offset 8524
+ link_argument[0] : 5 (MethodHandle)
+ link_argument[1] : targetMethodTest8 (String)
+ link_argument[2] : (Ljava/lang/String;)V (MethodType)
+Call site #11: // offset 8531
+ link_argument[0] : 6 (MethodHandle)
+ link_argument[1] : targetMethodTest3 (String)
+ link_argument[2] : ()V (MethodType)
link_argument[3] : 1 (int)
- link_argument[4] : 1 (int)
- link_argument[5] : 97 (int)
- link_argument[6] : 1024 (int)
- link_argument[7] : 1 (int)
- link_argument[8] : 11.1 (float)
- link_argument[9] : 2.2 (double)
- link_argument[10] : Hello (String)
- link_argument[11] : Tests (Class)
- link_argument[12] : 123456789 (long)
+ link_argument[4] : 123456789 (long)
+ link_argument[5] : 123.456 (float)
+ link_argument[6] : 123457 (double)
+Call site #12: // offset 8559
+ link_argument[0] : 4 (MethodHandle)
+ link_argument[1] : targetMethodTest4 (String)
+ link_argument[2] : (Linvokecustom/InvokeCustom;)V (MethodType)
+ link_argument[3] : 14 (MethodHandle)
+Call site #13: // offset 8568
+ link_argument[0] : 7 (MethodHandle)
+ link_argument[1] : targetMethodTest9 (String)
+ link_argument[2] : ()V (MethodType)
+ link_argument[3] : 1 (MethodHandle)
+ link_argument[4] : 0 (MethodHandle)
+ link_argument[5] : 3 (MethodHandle)
+ link_argument[6] : 2 (MethodHandle)
+ link_argument[7] : 10 (MethodHandle)
+ link_argument[8] : 13 (MethodHandle)
+ link_argument[9] : 15 (MethodHandle)
diff --git a/test/dexdump/invoke-custom.xml b/test/dexdump/invoke-custom.xml
index 2a2966738a..8b22a9d9ee 100644
--- a/test/dexdump/invoke-custom.xml
+++ b/test/dexdump/invoke-custom.xml
@@ -1,30 +1,130 @@
<api>
-<package name="com.android.jack.java7.invokecustom.test004"
+<package name="invokecustom"
>
-<class name="Tests"
- extends="java.lang.Object"
+<class name="InvokeCustom"
+ extends="invokecustom.Super"
interface="false"
abstract="false"
static="false"
final="false"
visibility="public"
>
-<field name="fieldCallSite"
- type="java.lang.invoke.CallSite"
- transient="false"
- volatile="false"
- static="true"
+<implements name="java.lang.Runnable">
+</implements>
+<constructor name="InvokeCustom"
+ type="invokecustom.InvokeCustom"
+ static="false"
final="false"
visibility="public"
>
-</field>
-<constructor name="Tests"
- type="com.android.jack.java7.invokecustom.test004.Tests"
+</constructor>
+<constructor name="InvokeCustom"
+ type="invokecustom.InvokeCustom"
static="false"
final="false"
visibility="public"
>
+<parameter name="arg0" type="int">
+</parameter>
</constructor>
+<method name="bsmCreateCallSite"
+ return="java.lang.invoke.CallSite"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.invoke.MethodHandles.Lookup">
+</parameter>
+<parameter name="arg1" type="java.lang.String">
+</parameter>
+<parameter name="arg2" type="java.lang.invoke.MethodType">
+</parameter>
+<parameter name="arg3" type="java.lang.invoke.MethodHandle">
+</parameter>
+</method>
+<method name="bsmLookupStatic"
+ return="java.lang.invoke.CallSite"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.invoke.MethodHandles.Lookup">
+</parameter>
+<parameter name="arg1" type="java.lang.String">
+</parameter>
+<parameter name="arg2" type="java.lang.invoke.MethodType">
+</parameter>
+</method>
+<method name="bsmLookupStaticWithExtraArgs"
+ return="java.lang.invoke.CallSite"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.invoke.MethodHandles.Lookup">
+</parameter>
+<parameter name="arg1" type="java.lang.String">
+</parameter>
+<parameter name="arg2" type="java.lang.invoke.MethodType">
+</parameter>
+<parameter name="arg3" type="int">
+</parameter>
+<parameter name="arg4" type="long">
+</parameter>
+<parameter name="arg5" type="float">
+</parameter>
+<parameter name="arg6" type="double">
+</parameter>
+</method>
+<method name="bsmLookupTest9"
+ return="java.lang.invoke.CallSite"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.invoke.MethodHandles.Lookup">
+</parameter>
+<parameter name="arg1" type="java.lang.String">
+</parameter>
+<parameter name="arg2" type="java.lang.invoke.MethodType">
+</parameter>
+<parameter name="arg3" type="java.lang.invoke.MethodHandle">
+</parameter>
+<parameter name="arg4" type="java.lang.invoke.MethodHandle">
+</parameter>
+<parameter name="arg5" type="java.lang.invoke.MethodHandle">
+</parameter>
+<parameter name="arg6" type="java.lang.invoke.MethodHandle">
+</parameter>
+<parameter name="arg7" type="java.lang.invoke.MethodHandle">
+</parameter>
+<parameter name="arg8" type="java.lang.invoke.MethodHandle">
+</parameter>
+<parameter name="arg9" type="java.lang.invoke.MethodHandle">
+</parameter>
+</method>
+<method name="lambdaTest"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
<method name="main"
return="void"
abstract="false"
@@ -37,7 +137,177 @@
<parameter name="arg0" type="java.lang.String[]">
</parameter>
</method>
-<method name="test"
+<method name="targetMethodTest5"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="int">
+</parameter>
+<parameter name="arg1" type="int">
+</parameter>
+<parameter name="arg2" type="int">
+</parameter>
+</method>
+<method name="targetMethodTest6"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="long">
+</parameter>
+<parameter name="arg1" type="long">
+</parameter>
+<parameter name="arg2" type="long">
+</parameter>
+</method>
+<method name="targetMethodTest7"
+ return="double"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="float">
+</parameter>
+<parameter name="arg1" type="float">
+</parameter>
+<parameter name="arg2" type="double">
+</parameter>
+</method>
+<method name="targetMethodTest8"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+<parameter name="arg0" type="java.lang.String">
+</parameter>
+</method>
+<method name="test1"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test2"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test3"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test4"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test5"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test6"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test7"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test8"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="test9"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="helperMethodTest9"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="run"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ visibility="public"
+>
+</method>
+<method name="targetMethodTest4"
return="void"
abstract="false"
native="false"
@@ -49,41 +319,207 @@
</method>
</class>
<method_handle index="0"
+ type="put-static"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="staticFieldTest9"
+ target_member_type="I"
+>
+</method_handle>
+<method_handle index="1"
+ type="get-static"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="staticFieldTest9"
+ target_member_type="I"
+>
+</method_handle>
+<method_handle index="2"
+ type="put-instance"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="fieldTest9"
+ target_member_type="(Linvokecustom/InvokeCustom;"
+>
+</method_handle>
+<method_handle index="3"
+ type="get-instance"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="fieldTest9"
+ target_member_type="(Linvokecustom/InvokeCustom;"
+>
+</method_handle>
+<method_handle index="4"
type="invoke-static"
- target_class="Lcom/android/jack/java7/invokecustom/test004/Tests;"
- target_member="linkerMethod"
- target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="bsmCreateCallSite"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;"
>
</method_handle>
-<call_site index="0">
-<link_argument index="0" type="MethodHandle" value="0"/>
-<link_argument index="1" type="String" values="add"/>
-<link_argument index="2" type="MethodType" value="(II)I"/>
-<link_argument index="3" type="int" value="1"/>
-<link_argument index="4" type="int" value="1"/>
-<link_argument index="5" type="int" value="97"/>
-<link_argument index="6" type="int" value="1024"/>
-<link_argument index="7" type="int" value="1"/>
-<link_argument index="8" type="float" value="11.1"/>
-<link_argument index="9" type="double" value="2.2"/>
-<link_argument index="10" type="String" value="Hello"/>
-<link_argument index="11" type="Class" value="Tests"/>
-<link_argument index="12" type="long" value="123456789"/>
+<method_handle index="5"
+ type="invoke-static"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="bsmLookupStatic"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<method_handle index="6"
+ type="invoke-static"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="bsmLookupStaticWithExtraArgs"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;IJFD)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<method_handle index="7"
+ type="invoke-static"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="bsmLookupTest9"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<method_handle index="8"
+ type="invoke-static"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="lambda$lambdaTest$0"
+ target_member_type="(Ljava/lang/String;)Z"
+>
+</method_handle>
+<method_handle index="9"
+ type="invoke-static"
+ target_class="Ljava/lang/invoke/LambdaMetafactory;"
+ target_member="metafactory"
+ target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"
+>
+</method_handle>
+<method_handle index="10"
+ type="invoke-instance"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="helperMethodTest9"
+ target_member_type="(Linvokecustom/InvokeCustom;)V"
+>
+</method_handle>
+<method_handle index="11"
+ type="invoke-instance"
+ target_class="Ljava/io/PrintStream;"
+ target_member="println"
+ target_member_type="(Ljava/io/PrintStream;Ljava/lang/String;)V"
+>
+</method_handle>
+<method_handle index="12"
+ type="invoke-instance"
+ target_class="Ljava/lang/String;"
+ target_member="trim"
+ target_member_type="(Ljava/lang/String;)Ljava/lang/String;"
+>
+</method_handle>
+<method_handle index="13"
+ type="invoke-constructor"
+ target_class="Linvokecustom/InvokeCustom;"
+ target_member="<init>"
+ target_member_type="(Linvokecustom/InvokeCustom;I)V"
+>
+</method_handle>
+<method_handle index="14"
+ type="invoke-direct"
+ target_class="Linvokecustom/Super;"
+ target_member="targetMethodTest4"
+ target_member_type="(Linvokecustom/Super;)V"
+>
+</method_handle>
+<method_handle index="15"
+ type="invoke-interface"
+ target_class="Ljava/lang/Runnable;"
+ target_member="run"
+ target_member_type="(Ljava/lang/Runnable;)V"
+>
+</method_handle>
+<call_site index="0" offset="8450">
+<link_argument index="0" type="MethodHandle" value="9"/>
+<link_argument index="1" type="String" values="test"/>
+<link_argument index="2" type="MethodType" value="()Ljava/util/function/Predicate;"/>
+<link_argument index="3" type="MethodType" value="(Ljava/lang/Object;)Z"/>
+<link_argument index="4" type="MethodHandle" value="8"/>
+<link_argument index="5" type="MethodType" value="(Ljava/lang/String;)Z"/>
+</call_site>
+<call_site index="1" offset="8463">
+<link_argument index="0" type="MethodHandle" value="9"/>
+<link_argument index="1" type="String" values="apply"/>
+<link_argument index="2" type="MethodType" value="()Ljava/util/function/Function;"/>
+<link_argument index="3" type="MethodType" value="(Ljava/lang/Object;)Ljava/lang/Object;"/>
+<link_argument index="4" type="MethodHandle" value="12"/>
+<link_argument index="5" type="MethodType" value="(Ljava/lang/String;)Ljava/lang/String;"/>
+</call_site>
+<call_site index="2" offset="8476">
+<link_argument index="0" type="MethodHandle" value="9"/>
+<link_argument index="1" type="String" values="accept"/>
+<link_argument index="2" type="MethodType" value="(Ljava/io/PrintStream;)Ljava/util/function/Consumer;"/>
+<link_argument index="3" type="MethodType" value="(Ljava/lang/Object;)V"/>
+<link_argument index="4" type="MethodHandle" value="11"/>
+<link_argument index="5" type="MethodType" value="(Ljava/lang/String;)V"/>
+</call_site>
+<call_site index="3" offset="8489">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest1"/>
+<link_argument index="2" type="MethodType" value="()V"/>
+</call_site>
+<call_site index="4" offset="8496">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest2"/>
+<link_argument index="2" type="MethodType" value="(ZBCSIFJDLjava/lang/String;)V"/>
</call_site>
-<call_site index="1">
-<link_argument index="0" type="MethodHandle" value="0"/>
-<link_argument index="1" type="String" values="add"/>
-<link_argument index="2" type="MethodType" value="(II)I"/>
+<call_site index="5" offset="8503">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest5"/>
+<link_argument index="2" type="MethodType" value="(III)I"/>
+</call_site>
+<call_site index="6" offset="8510">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest6"/>
+<link_argument index="2" type="MethodType" value="(JJJ)J"/>
+</call_site>
+<call_site index="7" offset="8517">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest7"/>
+<link_argument index="2" type="MethodType" value="(FFD)D"/>
+</call_site>
+<call_site index="8" offset="8524">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest8"/>
+<link_argument index="2" type="MethodType" value="(Ljava/lang/String;)V"/>
+</call_site>
+<call_site index="9" offset="8524">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest8"/>
+<link_argument index="2" type="MethodType" value="(Ljava/lang/String;)V"/>
+</call_site>
+<call_site index="10" offset="8524">
+<link_argument index="0" type="MethodHandle" value="5"/>
+<link_argument index="1" type="String" values="targetMethodTest8"/>
+<link_argument index="2" type="MethodType" value="(Ljava/lang/String;)V"/>
+</call_site>
+<call_site index="11" offset="8531">
+<link_argument index="0" type="MethodHandle" value="6"/>
+<link_argument index="1" type="String" values="targetMethodTest3"/>
+<link_argument index="2" type="MethodType" value="()V"/>
<link_argument index="3" type="int" value="1"/>
-<link_argument index="4" type="int" value="1"/>
-<link_argument index="5" type="int" value="97"/>
-<link_argument index="6" type="int" value="1024"/>
-<link_argument index="7" type="int" value="1"/>
-<link_argument index="8" type="float" value="11.1"/>
-<link_argument index="9" type="double" value="2.2"/>
-<link_argument index="10" type="String" value="Hello"/>
-<link_argument index="11" type="Class" value="Tests"/>
-<link_argument index="12" type="long" value="123456789"/>
+<link_argument index="4" type="long" value="123456789"/>
+<link_argument index="5" type="float" value="123.456"/>
+<link_argument index="6" type="double" value="123457"/>
+</call_site>
+<call_site index="12" offset="8559">
+<link_argument index="0" type="MethodHandle" value="4"/>
+<link_argument index="1" type="String" values="targetMethodTest4"/>
+<link_argument index="2" type="MethodType" value="(Linvokecustom/InvokeCustom;)V"/>
+<link_argument index="3" type="MethodHandle" value="14"/>
+</call_site>
+<call_site index="13" offset="8568">
+<link_argument index="0" type="MethodHandle" value="7"/>
+<link_argument index="1" type="String" values="targetMethodTest9"/>
+<link_argument index="2" type="MethodType" value="()V"/>
+<link_argument index="3" type="MethodHandle" value="1"/>
+<link_argument index="4" type="MethodHandle" value="0"/>
+<link_argument index="5" type="MethodHandle" value="3"/>
+<link_argument index="6" type="MethodHandle" value="2"/>
+<link_argument index="7" type="MethodHandle" value="10"/>
+<link_argument index="8" type="MethodHandle" value="13"/>
+<link_argument index="9" type="MethodHandle" value="15"/>
</call_site>
</package>
</api>
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index d24edccd8b..ede485a81b 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -63,6 +63,7 @@ TEST_VDEX="n"
TEST_IS_NDEBUG="n"
APP_IMAGE="y"
JVMTI_STRESS="n"
+JVMTI_STEP_STRESS="n"
JVMTI_FIELD_STRESS="n"
JVMTI_TRACE_STRESS="n"
JVMTI_REDEFINE_STRESS="n"
@@ -163,6 +164,10 @@ while true; do
JVMTI_STRESS="y"
JVMTI_REDEFINE_STRESS="y"
shift
+ elif [ "x$1" = "x--jvmti-step-stress" ]; then
+ JVMTI_STRESS="y"
+ JVMTI_STEP_STRESS="y"
+ shift
elif [ "x$1" = "x--jvmti-field-stress" ]; then
JVMTI_STRESS="y"
JVMTI_FIELD_STRESS="y"
@@ -426,6 +431,9 @@ if [[ "$JVMTI_STRESS" = "y" ]]; then
if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
agent_args="${agent_args},field"
fi
+ if [[ "$JVMTI_STEP_STRESS" = "y" ]]; then
+ agent_args="${agent_args},step"
+ fi
if [[ "$JVMTI_TRACE_STRESS" = "y" ]]; then
agent_args="${agent_args},trace"
fi
diff --git a/test/knownfailures.json b/test/knownfailures.json
index db993b7f70..3939bd162f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -502,7 +502,7 @@
"645-checker-abs-simd",
"706-checker-scheduler"],
"description": ["Checker tests are not compatible with jvmti."],
- "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
},
{
"tests": [
@@ -510,7 +510,7 @@
"964-default-iface-init-gen"
],
"description": ["Tests that just take too long with jvmti-stress"],
- "variant": "jvmti-stress | redefine-stress | trace-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | step-stress"
},
{
"tests": [
@@ -538,10 +538,11 @@
"595-profile-saving",
"900-hello-plugin",
"909-attach-agent",
- "981-dedup-original-dex"
+ "981-dedup-original-dex",
+ "1900-track-alloc"
],
"description": ["Tests that require exact knowledge of the number of plugins and agents."],
- "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
},
{
"tests": [
@@ -579,7 +580,7 @@
"004-ThreadStress"
],
"description": "The thread stress test just takes too long with field-stress",
- "variant": "jvmti-stress | field-stress"
+ "variant": "jvmti-stress | field-stress | step-stress"
},
{
"tests": [
@@ -594,25 +595,12 @@
},
{
"tests": [
- "953-invoke-polymorphic-compiler"
- ],
- "description": "Test throws VerifyError when run with --build-with-javac-dx.",
- "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
- "bug": "b/62722425"
- },
- {
- "tests": [
- "458-checker-instruct-simplification",
- "536-checker-intrinsic-optimization",
- "565-checker-doublenegbitwise",
"567-checker-compare",
- "586-checker-null-array-get",
- "593-checker-boolean-2-integral-conv",
- "633-checker-rtp-getclass"
+ "988-method-trace"
],
- "description": "Checker tests failing when run with --build-with-javac-dx.",
+ "description": "Checker tests fail because desugar lowers Long.compare to lcmp",
"env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
- "bug": "b/62950048"
+ "bug": "b/63078894"
},
{
"tests": [
@@ -659,6 +647,11 @@
"env_vars": {"SANITIZE_HOST": "address"}
},
{
+ "tests": "141-class-unload",
+ "description": "Idk why this fails",
+ "env_vars": {"SANITIZE_HOST": "address"}
+ },
+ {
"tests": ["988-method-trace"],
"variant": "redefine-stress | jvmti-stress",
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
@@ -679,5 +672,12 @@
"description": [ "Flake on gcstress" ],
"bug": "b/62562923",
"variant": "gcstress & jit & target"
+ },
+ {
+ "tests": ["004-JniTest"],
+ "description": [ "Tests failing with --build-with-javac-dx since the new annotation",
+ "lookup changes" ],
+ "bug": "b/63089991",
+ "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}
}
]
diff --git a/test/run-test b/test/run-test
index 9fe149697a..486b465a89 100755
--- a/test/run-test
+++ b/test/run-test
@@ -145,6 +145,7 @@ gc_verify="false"
gc_stress="false"
jvmti_trace_stress="false"
jvmti_field_stress="false"
+jvmti_step_stress="false"
jvmti_redefine_stress="false"
strace="false"
always_clean="no"
@@ -242,6 +243,9 @@ while true; do
basic_verify="true"
gc_stress="true"
shift
+ elif [ "x$1" = "x--jvmti-step-stress" ]; then
+ jvmti_step_stress="true"
+ shift
elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
jvmti_redefine_stress="true"
shift
@@ -464,6 +468,9 @@ fi
if [ "$jvmti_redefine_stress" = "true" ]; then
run_args="${run_args} --no-app-image --jvmti-redefine-stress"
fi
+if [ "$jvmti_step_stress" = "true" ]; then
+ run_args="${run_args} --no-app-image --jvmti-step-stress"
+fi
if [ "$jvmti_field_stress" = "true" ]; then
run_args="${run_args} --no-app-image --jvmti-field-stress"
fi
@@ -679,6 +686,7 @@ if [ "$usage" = "yes" ]; then
echo " --gcstress Run with gc stress testing"
echo " --gcverify Run with gc verification"
echo " --jvmti-trace-stress Run with jvmti method tracing stress testing"
+ echo " --jvmti-step-stress Run with jvmti single step stress testing"
echo " --jvmti-redefine-stress"
echo " Run with jvmti method redefinition stress testing"
echo " --always-clean Delete the test files even if the test fails."
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index b6a5963cf4..68e1856adb 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -148,7 +148,7 @@ def gather_test_info():
VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'}
VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress', 'redefine-stress', 'trace-stress',
- 'field-stress'}
+ 'field-stress', 'step-stress'}
VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'optimizing',
'regalloc_gc', 'speed-profile'}
@@ -445,6 +445,8 @@ def run_tests(tests):
options_test += ' --jvmti-trace-stress'
elif jvmti == 'redefine-stress':
options_test += ' --jvmti-redefine-stress'
+ elif jvmti == 'step-stress':
+ options_test += ' --jvmti-step-stress'
if address_size == '64':
options_test += ' --64'
@@ -965,6 +967,8 @@ def parse_option():
JVMTI_TYPES.add('redefine-stress')
if options['field_stress']:
JVMTI_TYPES.add('field-stress')
+ if options['step_stress']:
+ JVMTI_TYPES.add('step-stress')
if options['trace_stress']:
JVMTI_TYPES.add('trace-stress')
if options['no_jvmti']:
diff --git a/test/ti-agent/breakpoint_helper.cc b/test/ti-agent/breakpoint_helper.cc
new file mode 100644
index 0000000000..78aab4376f
--- /dev/null
+++ b/test/ti-agent/breakpoint_helper.cc
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 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 "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+
+namespace common_breakpoint {
+
+struct BreakpointData {
+ jclass test_klass;
+ jmethodID breakpoint_method;
+ bool in_callback;
+ bool allow_recursive;
+};
+
+extern "C" void breakpointCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ BreakpointData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback && !data->allow_recursive) {
+ return;
+ }
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->breakpoint_method,
+ thread,
+ method_arg,
+ static_cast<jlong>(location));
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Breakpoint_getLineNumberTableNative(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jint nlines;
+ jvmtiLineNumberEntry* lines = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetLineNumberTable(method, &nlines, &lines))) {
+ return nullptr;
+ }
+ jintArray lines_array = env->NewIntArray(nlines);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jlongArray locs_array = env->NewLongArray(nlines);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> object_class(env, env->FindClass("java/lang/Object"));
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jobjectArray ret = env->NewObjectArray(2, object_class.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jint* temp_lines = env->GetIntArrayElements(lines_array, /*isCopy*/nullptr);
+ jlong* temp_locs = env->GetLongArrayElements(locs_array, /*isCopy*/nullptr);
+ for (jint i = 0; i < nlines; i++) {
+ temp_lines[i] = lines[i].line_number;
+ temp_locs[i] = lines[i].start_location;
+ }
+ env->ReleaseIntArrayElements(lines_array, temp_lines, 0);
+ env->ReleaseLongArrayElements(locs_array, temp_locs, 0);
+ env->SetObjectArrayElement(ret, 0, locs_array);
+ env->SetObjectArrayElement(ret, 1, lines_array);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Breakpoint_getStartLocation(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return 0;
+ }
+ jlong start = 0;
+ jlong end = end;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
+ return start;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_clearBreakpoint(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target,
+ jlocation location) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->ClearBreakpoint(method, location));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_setBreakpoint(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target,
+ jlocation location) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, location));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_startBreakpointWatch(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jclass method_klass,
+ jobject method,
+ jboolean allow_recursive,
+ jthread thr) {
+ BreakpointData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(BreakpointData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(BreakpointData));
+ data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass));
+ data->breakpoint_method = env->FromReflectedMethod(method);
+ data->in_callback = false;
+ data->allow_recursive = allow_recursive;
+
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
+ return;
+ }
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.Breakpoint = breakpointCB;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_stopBreakpointWatch(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jthread thr) {
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+}
+
+} // namespace common_breakpoint
+
+} // namespace art
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 4fe58db169..e57a493fe4 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -16,76 +16,18 @@
#include "common_helper.h"
-#include <dlfcn.h>
-#include <map>
-#include <stdio.h>
#include <sstream>
-#include <deque>
-#include <vector>
+#include <string>
#include "android-base/stringprintf.h"
#include "jni.h"
#include "jvmti.h"
-#include "jni_binder.h"
#include "jvmti_helper.h"
-#include "scoped_local_ref.h"
-#include "test_env.h"
namespace art {
-static void SetupCommonRetransform();
-static void SetupCommonRedefine();
-static void SetupCommonTransform();
-
-template <bool is_redefine>
-static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
- JNIEnv* env,
- jint num_targets,
- jclass* target,
- jvmtiError res) {
- std::stringstream err;
- char* error = nullptr;
- jvmti->GetErrorName(res, &error);
- err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
- if (num_targets > 1) {
- err << "es";
- }
- err << " <";
- for (jint i = 0; i < num_targets; i++) {
- char* signature = nullptr;
- char* generic = nullptr;
- jvmti->GetClassSignature(target[i], &signature, &generic);
- if (i != 0) {
- err << ", ";
- }
- err << signature;
- jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
- jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
- }
- err << "> due to " << error;
- std::string message = err.str();
- jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
- env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
-}
-
-namespace common_trace {
-
-// Taken from art/runtime/modifiers.h
-static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
-
-struct TraceData {
- jclass test_klass;
- jmethodID enter_method;
- jmethodID exit_method;
- jmethodID field_access;
- jmethodID field_modify;
- bool in_callback;
- bool access_watch_on_load;
- bool modify_watch_on_load;
-};
-
-static jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f) {
+jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f) {
jint mods = 0;
if (JvmtiErrorToException(env, jvmti, jvmti->GetFieldModifiers(field_klass, f, &mods))) {
return nullptr;
@@ -95,7 +37,7 @@ static jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jf
return env->ToReflectedField(field_klass, f, is_static);
}
-static jobject GetJavaMethod(jvmtiEnv* jvmti, JNIEnv* env, jmethodID m) {
+jobject GetJavaMethod(jvmtiEnv* jvmti, JNIEnv* env, jmethodID m) {
jint mods = 0;
if (JvmtiErrorToException(env, jvmti, jvmti->GetMethodModifiers(m, &mods))) {
return nullptr;
@@ -111,7 +53,7 @@ static jobject GetJavaMethod(jvmtiEnv* jvmti, JNIEnv* env, jmethodID m) {
return res;
}
-static jobject GetJavaValueByType(JNIEnv* env, char type, jvalue value) {
+jobject GetJavaValueByType(JNIEnv* env, char type, jvalue value) {
std::string name;
switch (type) {
case 'V':
@@ -159,10 +101,7 @@ static jobject GetJavaValueByType(JNIEnv* env, char type, jvalue value) {
return res;
}
-static jobject GetJavaValue(jvmtiEnv* jvmtienv,
- JNIEnv* env,
- jmethodID m,
- jvalue value) {
+jobject GetJavaValue(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m, jvalue value) {
char *fname, *fsig, *fgen;
if (JvmtiErrorToException(env, jvmtienv, jvmtienv->GetMethodName(m, &fname, &fsig, &fgen))) {
return nullptr;
@@ -175,745 +114,4 @@ static jobject GetJavaValue(jvmtiEnv* jvmtienv,
return GetJavaValueByType(env, type[0], value);
}
-static void fieldAccessCB(jvmtiEnv* jvmti,
- JNIEnv* jnienv,
- jthread thr ATTRIBUTE_UNUSED,
- jmethodID method,
- jlocation location,
- jclass field_klass,
- jobject object,
- jfieldID field) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(jnienv, jvmti,
- jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- if (data->in_callback) {
- // Don't do callback for either of these to prevent an infinite loop.
- return;
- }
- CHECK(data->field_access != nullptr);
- data->in_callback = true;
- jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
- jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
- jnienv->CallStaticVoidMethod(data->test_klass,
- data->field_access,
- method_arg,
- static_cast<jlong>(location),
- field_klass,
- object,
- field_arg);
- jnienv->DeleteLocalRef(method_arg);
- jnienv->DeleteLocalRef(field_arg);
- data->in_callback = false;
-}
-
-static void fieldModificationCB(jvmtiEnv* jvmti,
- JNIEnv* jnienv,
- jthread thr ATTRIBUTE_UNUSED,
- jmethodID method,
- jlocation location,
- jclass field_klass,
- jobject object,
- jfieldID field,
- char type_char,
- jvalue new_value) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(jnienv, jvmti,
- jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- if (data->in_callback) {
- // Don't do callback recursively to prevent an infinite loop.
- return;
- }
- CHECK(data->field_modify != nullptr);
- data->in_callback = true;
- jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
- jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
- jobject value = GetJavaValueByType(jnienv, type_char, new_value);
- if (jnienv->ExceptionCheck()) {
- data->in_callback = false;
- jnienv->DeleteLocalRef(method_arg);
- jnienv->DeleteLocalRef(field_arg);
- return;
- }
- jnienv->CallStaticVoidMethod(data->test_klass,
- data->field_modify,
- method_arg,
- static_cast<jlong>(location),
- field_klass,
- object,
- field_arg,
- value);
- jnienv->DeleteLocalRef(method_arg);
- jnienv->DeleteLocalRef(field_arg);
- data->in_callback = false;
-}
-
-static void methodExitCB(jvmtiEnv* jvmti,
- JNIEnv* jnienv,
- jthread thr ATTRIBUTE_UNUSED,
- jmethodID method,
- jboolean was_popped_by_exception,
- jvalue return_value) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(jnienv, jvmti,
- jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- if (method == data->exit_method || method == data->enter_method || data->in_callback) {
- // Don't do callback for either of these to prevent an infinite loop.
- return;
- }
- CHECK(data->exit_method != nullptr);
- data->in_callback = true;
- jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
- jobject result =
- was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value);
- if (jnienv->ExceptionCheck()) {
- data->in_callback = false;
- return;
- }
- jnienv->CallStaticVoidMethod(data->test_klass,
- data->exit_method,
- method_arg,
- was_popped_by_exception,
- result);
- jnienv->DeleteLocalRef(method_arg);
- data->in_callback = false;
-}
-
-static void methodEntryCB(jvmtiEnv* jvmti,
- JNIEnv* jnienv,
- jthread thr ATTRIBUTE_UNUSED,
- jmethodID method) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(jnienv, jvmti,
- jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- CHECK(data->enter_method != nullptr);
- if (method == data->exit_method || method == data->enter_method || data->in_callback) {
- // Don't do callback for either of these to prevent an infinite loop.
- return;
- }
- data->in_callback = true;
- jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
- if (jnienv->ExceptionCheck()) {
- return;
- }
- jnienv->CallStaticVoidMethod(data->test_klass, data->enter_method, method_arg);
- jnienv->DeleteLocalRef(method_arg);
- data->in_callback = false;
-}
-
-static void classPrepareCB(jvmtiEnv* jvmti,
- JNIEnv* jnienv,
- jthread thr ATTRIBUTE_UNUSED,
- jclass klass) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(jnienv, jvmti,
- jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- if (data->access_watch_on_load || data->modify_watch_on_load) {
- jint nfields;
- jfieldID* fields;
- if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) {
- return;
- }
- for (jint i = 0; i < nfields; i++) {
- jfieldID f = fields[i];
- // Ignore errors
- if (data->access_watch_on_load) {
- jvmti->SetFieldAccessWatch(klass, f);
- }
-
- if (data->modify_watch_on_load) {
- jvmti->SetFieldModificationWatch(klass, f);
- }
- }
- jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
- }
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(
- env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- data->access_watch_on_load = true;
- // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
- if (JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
- JVMTI_EVENT_CLASS_PREPARE,
- nullptr))) {
- return;
- }
- jint nklasses;
- jclass* klasses;
- if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
- return;
- }
- for (jint i = 0; i < nklasses; i++) {
- jclass k = klasses[i];
-
- jint nfields;
- jfieldID* fields;
- jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
- if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
- continue;
- } else if (JvmtiErrorToException(env, jvmti_env, err)) {
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
- return;
- }
- for (jint j = 0; j < nfields; j++) {
- jvmti_env->SetFieldAccessWatch(k, fields[j]);
- }
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
- }
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(
- env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
- return;
- }
- data->modify_watch_on_load = true;
- // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
- if (JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
- JVMTI_EVENT_CLASS_PREPARE,
- nullptr))) {
- return;
- }
- jint nklasses;
- jclass* klasses;
- if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
- return;
- }
- for (jint i = 0; i < nklasses; i++) {
- jclass k = klasses[i];
-
- jint nfields;
- jfieldID* fields;
- jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
- if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
- continue;
- } else if (JvmtiErrorToException(env, jvmti_env, err)) {
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
- return;
- }
- for (jint j = 0; j < nfields; j++) {
- jvmti_env->SetFieldModificationWatch(k, fields[j]);
- }
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
- }
- jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
-}
-
-static bool GetFieldAndClass(JNIEnv* env,
- jobject ref_field,
- jclass* out_klass,
- jfieldID* out_field) {
- *out_field = env->FromReflectedField(ref_field);
- if (env->ExceptionCheck()) {
- return false;
- }
- jclass field_klass = env->FindClass("java/lang/reflect/Field");
- if (env->ExceptionCheck()) {
- return false;
- }
- jmethodID get_declaring_class_method =
- env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;");
- if (env->ExceptionCheck()) {
- env->DeleteLocalRef(field_klass);
- return false;
- }
- *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method));
- if (env->ExceptionCheck()) {
- *out_klass = nullptr;
- env->DeleteLocalRef(field_klass);
- return false;
- }
- env->DeleteLocalRef(field_klass);
- return true;
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
- JNIEnv* env,
- jclass trace ATTRIBUTE_UNUSED,
- jobject field_obj) {
- jfieldID field;
- jclass klass;
- if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
- return;
- }
-
- JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field));
- env->DeleteLocalRef(klass);
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
- JNIEnv* env,
- jclass trace ATTRIBUTE_UNUSED,
- jobject field_obj) {
- jfieldID field;
- jclass klass;
- if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
- return;
- }
- JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field));
- env->DeleteLocalRef(klass);
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
- JNIEnv* env,
- jclass trace ATTRIBUTE_UNUSED,
- jclass klass,
- jobject enter,
- jobject exit,
- jobject field_access,
- jobject field_modify,
- jthread thr) {
- TraceData* data = nullptr;
- if (JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->Allocate(sizeof(TraceData),
- reinterpret_cast<unsigned char**>(&data)))) {
- return;
- }
- memset(data, 0, sizeof(TraceData));
- data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
- data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
- data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
- data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
- data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
- data->in_callback = false;
-
- if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
- return;
- }
-
- jvmtiEventCallbacks cb;
- memset(&cb, 0, sizeof(cb));
- cb.MethodEntry = methodEntryCB;
- cb.MethodExit = methodExitCB;
- cb.FieldAccess = fieldAccessCB;
- cb.FieldModification = fieldModificationCB;
- cb.ClassPrepare = classPrepareCB;
- if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
- return;
- }
- if (enter != nullptr &&
- JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
- JVMTI_EVENT_METHOD_ENTRY,
- thr))) {
- return;
- }
- if (exit != nullptr &&
- JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
- JVMTI_EVENT_METHOD_EXIT,
- thr))) {
- return;
- }
- if (field_access != nullptr &&
- JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
- JVMTI_EVENT_FIELD_ACCESS,
- thr))) {
- return;
- }
- if (field_modify != nullptr &&
- JvmtiErrorToException(env,
- jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
- JVMTI_EVENT_FIELD_MODIFICATION,
- thr))) {
- return;
- }
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
- JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
- if (JvmtiErrorToException(env, jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
- JVMTI_EVENT_FIELD_ACCESS,
- thr))) {
- return;
- }
- if (JvmtiErrorToException(env, jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
- JVMTI_EVENT_FIELD_MODIFICATION,
- thr))) {
- return;
- }
- if (JvmtiErrorToException(env, jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
- JVMTI_EVENT_METHOD_ENTRY,
- thr))) {
- return;
- }
- if (JvmtiErrorToException(env, jvmti_env,
- jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
- JVMTI_EVENT_METHOD_EXIT,
- thr))) {
- return;
- }
-}
-
-} // namespace common_trace
-
-namespace common_redefine {
-
-static void throwRedefinitionError(jvmtiEnv* jvmti,
- JNIEnv* env,
- jint num_targets,
- jclass* target,
- jvmtiError res) {
- return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
-}
-
-static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
- JNIEnv* env,
- jint num_redefines,
- jclass* targets,
- jbyteArray* class_file_bytes,
- jbyteArray* dex_file_bytes) {
- std::vector<jvmtiClassDefinition> defs;
- for (jint i = 0; i < num_redefines; i++) {
- jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
- jint len = static_cast<jint>(env->GetArrayLength(desired_array));
- const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
- env->GetByteArrayElements(desired_array, nullptr));
- defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
- }
- jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
- if (res != JVMTI_ERROR_NONE) {
- throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
- }
-}
-
-static void DoClassRedefine(jvmtiEnv* jvmti_env,
- JNIEnv* env,
- jclass target,
- jbyteArray class_file_bytes,
- jbyteArray dex_file_bytes) {
- return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
-}
-
-// Magic JNI export that classes can use for redefining classes.
-// To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
-extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
- JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
- DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
-}
-
-// Magic JNI export that classes can use for redefining classes.
-// To use classes should declare this as a native function with signature
-// ([Ljava/lang/Class;[[B[[B)V
-extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefinition(
- JNIEnv* env,
- jclass,
- jobjectArray targets,
- jobjectArray class_file_bytes,
- jobjectArray dex_file_bytes) {
- std::vector<jclass> classes;
- std::vector<jbyteArray> class_files;
- std::vector<jbyteArray> dex_files;
- jint len = env->GetArrayLength(targets);
- if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
- env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
- "the three array arguments passed to this function have different lengths!");
- return;
- }
- for (jint i = 0; i < len; i++) {
- classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
- dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
- class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
- }
- return DoMultiClassRedefine(jvmti_env,
- env,
- len,
- classes.data(),
- class_files.data(),
- dex_files.data());
-}
-
-// Get all capabilities except those related to retransformation.
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetupCommonRedefine();
- return 0;
-}
-
-} // namespace common_redefine
-
-namespace common_retransform {
-
-struct CommonTransformationResult {
- std::vector<unsigned char> class_bytes;
- std::vector<unsigned char> dex_bytes;
-
- CommonTransformationResult(size_t class_size, size_t dex_size)
- : class_bytes(class_size), dex_bytes(dex_size) {}
-
- CommonTransformationResult() = default;
- CommonTransformationResult(CommonTransformationResult&&) = default;
- CommonTransformationResult(CommonTransformationResult&) = default;
-};
-
-// Map from class name to transformation result.
-std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
-bool gPopTransformations = true;
-
-extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_addCommonTransformationResult(
- JNIEnv* env, jclass, jstring class_name, jbyteArray class_array, jbyteArray dex_array) {
- const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
- std::string name_str(name_chrs);
- env->ReleaseStringUTFChars(class_name, name_chrs);
- CommonTransformationResult trans(env->GetArrayLength(class_array),
- env->GetArrayLength(dex_array));
- if (env->ExceptionOccurred()) {
- return;
- }
- env->GetByteArrayRegion(class_array,
- 0,
- env->GetArrayLength(class_array),
- reinterpret_cast<jbyte*>(trans.class_bytes.data()));
- if (env->ExceptionOccurred()) {
- return;
- }
- env->GetByteArrayRegion(dex_array,
- 0,
- env->GetArrayLength(dex_array),
- reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
- if (env->ExceptionOccurred()) {
- return;
- }
- if (gTransformations.find(name_str) == gTransformations.end()) {
- std::deque<CommonTransformationResult> list;
- gTransformations[name_str] = std::move(list);
- }
- gTransformations[name_str].push_back(std::move(trans));
-}
-
-// The hook we are using.
-void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
- JNIEnv* jni_env ATTRIBUTE_UNUSED,
- jclass class_being_redefined ATTRIBUTE_UNUSED,
- jobject loader ATTRIBUTE_UNUSED,
- const char* name,
- jobject protection_domain ATTRIBUTE_UNUSED,
- jint class_data_len ATTRIBUTE_UNUSED,
- const unsigned char* class_dat ATTRIBUTE_UNUSED,
- jint* new_class_data_len,
- unsigned char** new_class_data) {
- std::string name_str(name);
- if (gTransformations.find(name_str) != gTransformations.end() &&
- gTransformations[name_str].size() > 0) {
- CommonTransformationResult& res = gTransformations[name_str][0];
- const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
- unsigned char* new_data;
- CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
- memcpy(new_data, desired_array.data(), desired_array.size());
- *new_class_data = new_data;
- *new_class_data_len = desired_array.size();
- if (gPopTransformations) {
- gTransformations[name_str].pop_front();
- }
- }
-}
-
-extern "C" JNIEXPORT void Java_art_Redefinition_setPopRetransformations(JNIEnv*,
- jclass,
- jboolean enable) {
- gPopTransformations = enable;
-}
-
-extern "C" JNIEXPORT void Java_art_Redefinition_popTransformationFor(JNIEnv* env,
- jclass,
- jstring class_name) {
- const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
- std::string name_str(name_chrs);
- env->ReleaseStringUTFChars(class_name, name_chrs);
- if (gTransformations.find(name_str) != gTransformations.end() &&
- gTransformations[name_str].size() > 0) {
- gTransformations[name_str].pop_front();
- } else {
- std::stringstream err;
- err << "No transformations found for class " << name_str;
- std::string message = err.str();
- env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
- }
-}
-
-extern "C" JNIEXPORT void Java_art_Redefinition_enableCommonRetransformation(JNIEnv* env,
- jclass,
- jboolean enable) {
- jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
- JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
- nullptr);
- if (res != JVMTI_ERROR_NONE) {
- JvmtiErrorToException(env, jvmti_env, res);
- }
-}
-
-static void throwRetransformationError(jvmtiEnv* jvmti,
- JNIEnv* env,
- jint num_targets,
- jclass* targets,
- jvmtiError res) {
- return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
-}
-
-static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
- std::vector<jclass> classes;
- jint len = env->GetArrayLength(targets);
- for (jint i = 0; i < len; i++) {
- classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
- }
- jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
- if (res != JVMTI_ERROR_NONE) {
- throwRetransformationError(jvmti_env, env, len, classes.data(), res);
- }
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransformation(
- JNIEnv* env, jclass, jobjectArray targets) {
- jvmtiCapabilities caps;
- jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
- if (caps_err != JVMTI_ERROR_NONE) {
- env->ThrowNew(env->FindClass("java/lang/Exception"),
- "Unable to get current jvmtiEnv capabilities");
- return;
- }
-
- // Allocate a new environment if we don't have the can_retransform_classes capability needed to
- // call the RetransformClasses function.
- jvmtiEnv* real_env = nullptr;
- if (caps.can_retransform_classes != 1) {
- JavaVM* vm = nullptr;
- if (env->GetJavaVM(&vm) != 0 ||
- vm->GetEnv(reinterpret_cast<void**>(&real_env), JVMTI_VERSION_1_0) != 0) {
- env->ThrowNew(env->FindClass("java/lang/Exception"),
- "Unable to create temporary jvmtiEnv for RetransformClasses call.");
- return;
- }
- SetAllCapabilities(real_env);
- } else {
- real_env = jvmti_env;
- }
- DoClassRetransformation(real_env, env, targets);
- if (caps.can_retransform_classes != 1) {
- real_env->DisposeEnvironment();
- }
-}
-
-// Get all capabilities except those related to retransformation.
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetupCommonRetransform();
- return 0;
-}
-
-} // namespace common_retransform
-
-namespace common_transform {
-
-// Get all capabilities except those related to retransformation.
-jint OnLoad(JavaVM* vm,
- char* options ATTRIBUTE_UNUSED,
- void* reserved ATTRIBUTE_UNUSED) {
- if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
- printf("Unable to get jvmti env!\n");
- return 1;
- }
- SetupCommonTransform();
- return 0;
-}
-
-} // namespace common_transform
-
-#define CONFIGURATION_COMMON_REDEFINE 0
-#define CONFIGURATION_COMMON_RETRANSFORM 1
-#define CONFIGURATION_COMMON_TRANSFORM 2
-
-static void SetupCommonRedefine() {
- jvmtiCapabilities caps;
- jvmti_env->GetPotentialCapabilities(&caps);
- caps.can_retransform_classes = 0;
- caps.can_retransform_any_class = 0;
- jvmti_env->AddCapabilities(&caps);
-}
-
-static void SetupCommonRetransform() {
- SetAllCapabilities(jvmti_env);
- jvmtiEventCallbacks cb;
- memset(&cb, 0, sizeof(cb));
- cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
- jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
- CHECK_EQ(res, JVMTI_ERROR_NONE);
- common_retransform::gTransformations.clear();
-}
-
-static void SetupCommonTransform() {
- // Don't set the retransform caps
- jvmtiCapabilities caps;
- jvmti_env->GetPotentialCapabilities(&caps);
- caps.can_retransform_classes = 0;
- caps.can_retransform_any_class = 0;
- jvmti_env->AddCapabilities(&caps);
-
- // Use the same callback as the retransform test.
- jvmtiEventCallbacks cb;
- memset(&cb, 0, sizeof(cb));
- cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
- jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
- CHECK_EQ(res, JVMTI_ERROR_NONE);
- common_retransform::gTransformations.clear();
-}
-
-extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
- jclass,
- jint type) {
- switch (type) {
- case CONFIGURATION_COMMON_REDEFINE: {
- SetupCommonRedefine();
- return;
- }
- case CONFIGURATION_COMMON_RETRANSFORM: {
- SetupCommonRetransform();
- return;
- }
- case CONFIGURATION_COMMON_TRANSFORM: {
- SetupCommonTransform();
- return;
- }
- default: {
- LOG(FATAL) << "Unknown test configuration: " << type;
- }
- }
-}
} // namespace art
diff --git a/test/ti-agent/common_helper.h b/test/ti-agent/common_helper.h
index 610019e4d2..fafa1afcda 100644
--- a/test/ti-agent/common_helper.h
+++ b/test/ti-agent/common_helper.h
@@ -22,17 +22,13 @@
namespace art {
-namespace common_redefine {
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-} // namespace common_redefine
+// Taken from art/runtime/modifiers.h
+static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
-namespace common_retransform {
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-} // namespace common_retransform
-
-namespace common_transform {
-jint OnLoad(JavaVM* vm, char* options, void* reserved);
-} // namespace common_transform
+jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f);
+jobject GetJavaMethod(jvmtiEnv* jvmti, JNIEnv* env, jmethodID m);
+jobject GetJavaValueByType(JNIEnv* env, char type, jvalue value);
+jobject GetJavaValue(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m, jvalue value);
} // namespace art
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index fd47f59905..0679c1bc17 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -20,7 +20,6 @@
#include "base/logging.h"
#include "base/macros.h"
-#include "common_helper.h"
#include "jni_binder.h"
#include "jvmti_helper.h"
#include "test_env.h"
@@ -32,6 +31,18 @@
namespace art {
+namespace common_redefine {
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+} // namespace common_redefine
+
+namespace common_retransform {
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+} // namespace common_retransform
+
+namespace common_transform {
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+} // namespace common_transform
+
namespace {
using OnLoad = jint (*)(JavaVM* vm, char* options, void* reserved);
diff --git a/test/ti-agent/redefinition_helper.cc b/test/ti-agent/redefinition_helper.cc
new file mode 100644
index 0000000000..3b18879ca5
--- /dev/null
+++ b/test/ti-agent/redefinition_helper.cc
@@ -0,0 +1,410 @@
+/*
+ * 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 "common_helper.h"
+
+#include <deque>
+#include <map>
+#include <stdio.h>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+
+static void SetupCommonRedefine();
+static void SetupCommonRetransform();
+static void SetupCommonTransform();
+template <bool is_redefine>
+static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jint num_targets,
+ jclass* target,
+ jvmtiError res) {
+ std::stringstream err;
+ char* error = nullptr;
+ jvmti->GetErrorName(res, &error);
+ err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
+ if (num_targets > 1) {
+ err << "es";
+ }
+ err << " <";
+ for (jint i = 0; i < num_targets; i++) {
+ char* signature = nullptr;
+ char* generic = nullptr;
+ jvmti->GetClassSignature(target[i], &signature, &generic);
+ if (i != 0) {
+ err << ", ";
+ }
+ err << signature;
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
+ }
+ err << "> due to " << error;
+ std::string message = err.str();
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
+ env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
+}
+
+#define CONFIGURATION_COMMON_REDEFINE 0
+#define CONFIGURATION_COMMON_RETRANSFORM 1
+#define CONFIGURATION_COMMON_TRANSFORM 2
+
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
+ jclass,
+ jint type) {
+ switch (type) {
+ case CONFIGURATION_COMMON_REDEFINE: {
+ SetupCommonRedefine();
+ return;
+ }
+ case CONFIGURATION_COMMON_RETRANSFORM: {
+ SetupCommonRetransform();
+ return;
+ }
+ case CONFIGURATION_COMMON_TRANSFORM: {
+ SetupCommonTransform();
+ return;
+ }
+ default: {
+ LOG(FATAL) << "Unknown test configuration: " << type;
+ }
+ }
+}
+
+namespace common_redefine {
+
+static void throwRedefinitionError(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jint num_targets,
+ jclass* target,
+ jvmtiError res) {
+ return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
+}
+
+static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
+ JNIEnv* env,
+ jint num_redefines,
+ jclass* targets,
+ jbyteArray* class_file_bytes,
+ jbyteArray* dex_file_bytes) {
+ std::vector<jvmtiClassDefinition> defs;
+ for (jint i = 0; i < num_redefines; i++) {
+ jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
+ jint len = static_cast<jint>(env->GetArrayLength(desired_array));
+ const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
+ env->GetByteArrayElements(desired_array, nullptr));
+ defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
+ }
+ jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
+ if (res != JVMTI_ERROR_NONE) {
+ throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
+ }
+}
+
+static void DoClassRedefine(jvmtiEnv* jvmti_env,
+ JNIEnv* env,
+ jclass target,
+ jbyteArray class_file_bytes,
+ jbyteArray dex_file_bytes) {
+ return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
+}
+
+// Magic JNI export that classes can use for redefining classes.
+// To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
+ JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
+ DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
+}
+
+// Magic JNI export that classes can use for redefining classes.
+// To use classes should declare this as a native function with signature
+// ([Ljava/lang/Class;[[B[[B)V
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefinition(
+ JNIEnv* env,
+ jclass,
+ jobjectArray targets,
+ jobjectArray class_file_bytes,
+ jobjectArray dex_file_bytes) {
+ std::vector<jclass> classes;
+ std::vector<jbyteArray> class_files;
+ std::vector<jbyteArray> dex_files;
+ jint len = env->GetArrayLength(targets);
+ if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
+ env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
+ "the three array arguments passed to this function have different lengths!");
+ return;
+ }
+ for (jint i = 0; i < len; i++) {
+ classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
+ dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
+ class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
+ }
+ return DoMultiClassRedefine(jvmti_env,
+ env,
+ len,
+ classes.data(),
+ class_files.data(),
+ dex_files.data());
+}
+
+// Get all capabilities except those related to retransformation.
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ SetupCommonRedefine();
+ return 0;
+}
+
+} // namespace common_redefine
+
+namespace common_retransform {
+
+struct CommonTransformationResult {
+ std::vector<unsigned char> class_bytes;
+ std::vector<unsigned char> dex_bytes;
+
+ CommonTransformationResult(size_t class_size, size_t dex_size)
+ : class_bytes(class_size), dex_bytes(dex_size) {}
+
+ CommonTransformationResult() = default;
+ CommonTransformationResult(CommonTransformationResult&&) = default;
+ CommonTransformationResult(CommonTransformationResult&) = default;
+};
+
+// Map from class name to transformation result.
+std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
+bool gPopTransformations = true;
+
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_addCommonTransformationResult(
+ JNIEnv* env, jclass, jstring class_name, jbyteArray class_array, jbyteArray dex_array) {
+ const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
+ std::string name_str(name_chrs);
+ env->ReleaseStringUTFChars(class_name, name_chrs);
+ CommonTransformationResult trans(env->GetArrayLength(class_array),
+ env->GetArrayLength(dex_array));
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ env->GetByteArrayRegion(class_array,
+ 0,
+ env->GetArrayLength(class_array),
+ reinterpret_cast<jbyte*>(trans.class_bytes.data()));
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ env->GetByteArrayRegion(dex_array,
+ 0,
+ env->GetArrayLength(dex_array),
+ reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
+ if (env->ExceptionOccurred()) {
+ return;
+ }
+ if (gTransformations.find(name_str) == gTransformations.end()) {
+ std::deque<CommonTransformationResult> list;
+ gTransformations[name_str] = std::move(list);
+ }
+ gTransformations[name_str].push_back(std::move(trans));
+}
+
+// The hook we are using.
+void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
+ JNIEnv* jni_env ATTRIBUTE_UNUSED,
+ jclass class_being_redefined ATTRIBUTE_UNUSED,
+ jobject loader ATTRIBUTE_UNUSED,
+ const char* name,
+ jobject protection_domain ATTRIBUTE_UNUSED,
+ jint class_data_len ATTRIBUTE_UNUSED,
+ const unsigned char* class_dat ATTRIBUTE_UNUSED,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) {
+ std::string name_str(name);
+ if (gTransformations.find(name_str) != gTransformations.end() &&
+ gTransformations[name_str].size() > 0) {
+ CommonTransformationResult& res = gTransformations[name_str][0];
+ const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
+ unsigned char* new_data;
+ CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
+ memcpy(new_data, desired_array.data(), desired_array.size());
+ *new_class_data = new_data;
+ *new_class_data_len = desired_array.size();
+ if (gPopTransformations) {
+ gTransformations[name_str].pop_front();
+ }
+ }
+}
+
+extern "C" JNIEXPORT void Java_art_Redefinition_setPopRetransformations(JNIEnv*,
+ jclass,
+ jboolean enable) {
+ gPopTransformations = enable;
+}
+
+extern "C" JNIEXPORT void Java_art_Redefinition_popTransformationFor(JNIEnv* env,
+ jclass,
+ jstring class_name) {
+ const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
+ std::string name_str(name_chrs);
+ env->ReleaseStringUTFChars(class_name, name_chrs);
+ if (gTransformations.find(name_str) != gTransformations.end() &&
+ gTransformations[name_str].size() > 0) {
+ gTransformations[name_str].pop_front();
+ } else {
+ std::stringstream err;
+ err << "No transformations found for class " << name_str;
+ std::string message = err.str();
+ env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
+ }
+}
+
+extern "C" JNIEXPORT void Java_art_Redefinition_enableCommonRetransformation(JNIEnv* env,
+ jclass,
+ jboolean enable) {
+ jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ nullptr);
+ if (res != JVMTI_ERROR_NONE) {
+ JvmtiErrorToException(env, jvmti_env, res);
+ }
+}
+
+static void throwRetransformationError(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jint num_targets,
+ jclass* targets,
+ jvmtiError res) {
+ return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
+}
+
+static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
+ std::vector<jclass> classes;
+ jint len = env->GetArrayLength(targets);
+ for (jint i = 0; i < len; i++) {
+ classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
+ }
+ jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
+ if (res != JVMTI_ERROR_NONE) {
+ throwRetransformationError(jvmti_env, env, len, classes.data(), res);
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransformation(
+ JNIEnv* env, jclass, jobjectArray targets) {
+ jvmtiCapabilities caps;
+ jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
+ if (caps_err != JVMTI_ERROR_NONE) {
+ env->ThrowNew(env->FindClass("java/lang/Exception"),
+ "Unable to get current jvmtiEnv capabilities");
+ return;
+ }
+
+ // Allocate a new environment if we don't have the can_retransform_classes capability needed to
+ // call the RetransformClasses function.
+ jvmtiEnv* real_env = nullptr;
+ if (caps.can_retransform_classes != 1) {
+ JavaVM* vm = nullptr;
+ if (env->GetJavaVM(&vm) != 0 ||
+ vm->GetEnv(reinterpret_cast<void**>(&real_env), JVMTI_VERSION_1_0) != 0) {
+ env->ThrowNew(env->FindClass("java/lang/Exception"),
+ "Unable to create temporary jvmtiEnv for RetransformClasses call.");
+ return;
+ }
+ SetAllCapabilities(real_env);
+ } else {
+ real_env = jvmti_env;
+ }
+ DoClassRetransformation(real_env, env, targets);
+ if (caps.can_retransform_classes != 1) {
+ real_env->DisposeEnvironment();
+ }
+}
+
+// Get all capabilities except those related to retransformation.
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ SetupCommonRetransform();
+ return 0;
+}
+
+} // namespace common_retransform
+
+namespace common_transform {
+
+// Get all capabilities except those related to retransformation.
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ SetupCommonTransform();
+ return 0;
+}
+
+} // namespace common_transform
+
+static void SetupCommonRedefine() {
+ jvmtiCapabilities caps;
+ jvmti_env->GetPotentialCapabilities(&caps);
+ caps.can_retransform_classes = 0;
+ caps.can_retransform_any_class = 0;
+ jvmti_env->AddCapabilities(&caps);
+}
+
+static void SetupCommonRetransform() {
+ SetAllCapabilities(jvmti_env);
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
+ jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
+ CHECK_EQ(res, JVMTI_ERROR_NONE);
+ common_retransform::gTransformations.clear();
+}
+
+static void SetupCommonTransform() {
+ // Don't set the retransform caps
+ jvmtiCapabilities caps;
+ jvmti_env->GetPotentialCapabilities(&caps);
+ caps.can_retransform_classes = 0;
+ caps.can_retransform_any_class = 0;
+ jvmti_env->AddCapabilities(&caps);
+
+ // Use the same callback as the retransform test.
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
+ jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
+ CHECK_EQ(res, JVMTI_ERROR_NONE);
+ common_retransform::gTransformations.clear();
+}
+
+} // namespace art
diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc
new file mode 100644
index 0000000000..7a9d1e0d92
--- /dev/null
+++ b/test/ti-agent/trace_helper.cc
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2017 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 "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+
+namespace common_trace {
+
+struct TraceData {
+ jclass test_klass;
+ jmethodID enter_method;
+ jmethodID exit_method;
+ jmethodID field_access;
+ jmethodID field_modify;
+ jmethodID single_step;
+ bool in_callback;
+ bool access_watch_on_load;
+ bool modify_watch_on_load;
+};
+
+static void singleStepCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ return;
+ }
+ CHECK(data->single_step != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->single_step,
+ thread,
+ method_arg,
+ static_cast<jlong>(location));
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
+static void fieldAccessCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ // Don't do callback for either of these to prevent an infinite loop.
+ return;
+ }
+ CHECK(data->field_access != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->field_access,
+ method_arg,
+ static_cast<jlong>(location),
+ field_klass,
+ object,
+ field_arg);
+ jnienv->DeleteLocalRef(method_arg);
+ jnienv->DeleteLocalRef(field_arg);
+ data->in_callback = false;
+}
+
+static void fieldModificationCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field,
+ char type_char,
+ jvalue new_value) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ // Don't do callback recursively to prevent an infinite loop.
+ return;
+ }
+ CHECK(data->field_modify != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field);
+ jobject value = GetJavaValueByType(jnienv, type_char, new_value);
+ if (jnienv->ExceptionCheck()) {
+ data->in_callback = false;
+ jnienv->DeleteLocalRef(method_arg);
+ jnienv->DeleteLocalRef(field_arg);
+ return;
+ }
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->field_modify,
+ method_arg,
+ static_cast<jlong>(location),
+ field_klass,
+ object,
+ field_arg,
+ value);
+ jnienv->DeleteLocalRef(method_arg);
+ jnienv->DeleteLocalRef(field_arg);
+ data->in_callback = false;
+}
+
+static void methodExitCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jmethodID method,
+ jboolean was_popped_by_exception,
+ jvalue return_value) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (method == data->exit_method || method == data->enter_method || data->in_callback) {
+ // Don't do callback for either of these to prevent an infinite loop.
+ return;
+ }
+ CHECK(data->exit_method != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jobject result =
+ was_popped_by_exception ? nullptr : GetJavaValue(jvmti, jnienv, method, return_value);
+ if (jnienv->ExceptionCheck()) {
+ data->in_callback = false;
+ return;
+ }
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->exit_method,
+ method_arg,
+ was_popped_by_exception,
+ result);
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
+static void methodEntryCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jmethodID method) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data->enter_method != nullptr);
+ if (method == data->exit_method || method == data->enter_method || data->in_callback) {
+ // Don't do callback for either of these to prevent an infinite loop.
+ return;
+ }
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ if (jnienv->ExceptionCheck()) {
+ return;
+ }
+ jnienv->CallStaticVoidMethod(data->test_klass, data->enter_method, method_arg);
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
+static void classPrepareCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thr ATTRIBUTE_UNUSED,
+ jclass klass) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->access_watch_on_load || data->modify_watch_on_load) {
+ jint nfields;
+ jfieldID* fields;
+ if (JvmtiErrorToException(jnienv, jvmti, jvmti->GetClassFields(klass, &nfields, &fields))) {
+ return;
+ }
+ for (jint i = 0; i < nfields; i++) {
+ jfieldID f = fields[i];
+ // Ignore errors
+ if (data->access_watch_on_load) {
+ jvmti->SetFieldAccessWatch(klass, f);
+ }
+
+ if (data->modify_watch_on_load) {
+ jvmti->SetFieldModificationWatch(klass, f);
+ }
+ }
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldAccesses(JNIEnv* env) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ data->access_watch_on_load = true;
+ // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr))) {
+ return;
+ }
+ jint nklasses;
+ jclass* klasses;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
+ return;
+ }
+ for (jint i = 0; i < nklasses; i++) {
+ jclass k = klasses[i];
+
+ jint nfields;
+ jfieldID* fields;
+ jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
+ if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
+ continue;
+ } else if (JvmtiErrorToException(env, jvmti_env, err)) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+ return;
+ }
+ for (jint j = 0; j < nfields; j++) {
+ jvmti_env->SetFieldAccessWatch(k, fields[j]);
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchAllFieldModifications(JNIEnv* env) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ data->modify_watch_on_load = true;
+ // We need the classPrepareCB to watch new fields as the classes are loaded/prepared.
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ nullptr))) {
+ return;
+ }
+ jint nklasses;
+ jclass* klasses;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&nklasses, &klasses))) {
+ return;
+ }
+ for (jint i = 0; i < nklasses; i++) {
+ jclass k = klasses[i];
+
+ jint nfields;
+ jfieldID* fields;
+ jvmtiError err = jvmti_env->GetClassFields(k, &nfields, &fields);
+ if (err == JVMTI_ERROR_CLASS_NOT_PREPARED) {
+ continue;
+ } else if (JvmtiErrorToException(env, jvmti_env, err)) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+ return;
+ }
+ for (jint j = 0; j < nfields; j++) {
+ jvmti_env->SetFieldModificationWatch(k, fields[j]);
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields));
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+}
+
+static bool GetFieldAndClass(JNIEnv* env,
+ jobject ref_field,
+ jclass* out_klass,
+ jfieldID* out_field) {
+ *out_field = env->FromReflectedField(ref_field);
+ if (env->ExceptionCheck()) {
+ return false;
+ }
+ jclass field_klass = env->FindClass("java/lang/reflect/Field");
+ if (env->ExceptionCheck()) {
+ return false;
+ }
+ jmethodID get_declaring_class_method =
+ env->GetMethodID(field_klass, "getDeclaringClass", "()Ljava/lang/Class;");
+ if (env->ExceptionCheck()) {
+ env->DeleteLocalRef(field_klass);
+ return false;
+ }
+ *out_klass = static_cast<jclass>(env->CallObjectMethod(ref_field, get_declaring_class_method));
+ if (env->ExceptionCheck()) {
+ *out_klass = nullptr;
+ env->DeleteLocalRef(field_klass);
+ return false;
+ }
+ env->DeleteLocalRef(field_klass);
+ return true;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldModification(
+ JNIEnv* env,
+ jclass trace ATTRIBUTE_UNUSED,
+ jobject field_obj) {
+ jfieldID field;
+ jclass klass;
+ if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
+ return;
+ }
+
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(klass, field));
+ env->DeleteLocalRef(klass);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_watchFieldAccess(
+ JNIEnv* env,
+ jclass trace ATTRIBUTE_UNUSED,
+ jobject field_obj) {
+ jfieldID field;
+ jclass klass;
+ if (!GetFieldAndClass(env, field_obj, &klass, &field)) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(klass, field));
+ env->DeleteLocalRef(klass);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
+ JNIEnv* env,
+ jclass trace ATTRIBUTE_UNUSED,
+ jclass klass,
+ jobject enter,
+ jobject exit,
+ jobject field_access,
+ jobject field_modify,
+ jobject single_step,
+ jthread thr) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(TraceData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(TraceData));
+ data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
+ data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr;
+ data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
+ data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
+ data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
+ data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
+ data->in_callback = false;
+
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
+ return;
+ }
+
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.MethodEntry = methodEntryCB;
+ cb.MethodExit = methodExitCB;
+ cb.FieldAccess = fieldAccessCB;
+ cb.FieldModification = fieldModificationCB;
+ cb.ClassPrepare = classPrepareCB;
+ cb.SingleStep = singleStepCB;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+ return;
+ }
+ if (enter != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_METHOD_ENTRY,
+ thr))) {
+ return;
+ }
+ if (exit != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_METHOD_EXIT,
+ thr))) {
+ return;
+ }
+ if (field_access != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_FIELD_ACCESS,
+ thr))) {
+ return;
+ }
+ if (field_modify != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_FIELD_MODIFICATION,
+ thr))) {
+ return;
+ }
+ if (single_step != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FIELD_ACCESS,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FIELD_MODIFICATION,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_METHOD_ENTRY,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_METHOD_EXIT,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
+}
+
+} // namespace common_trace
+
+
+} // namespace art
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 40fcc4f11d..d197acd216 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -17,6 +17,7 @@
#include <jni.h>
#include <stdio.h>
#include <iostream>
+#include <iomanip>
#include <fstream>
#include <memory>
#include <stdio.h>
@@ -40,6 +41,7 @@ struct StressData {
bool trace_stress;
bool redefine_stress;
bool field_stress;
+ bool step_stress;
};
static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
@@ -586,6 +588,21 @@ void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv,
}
}
+void JNICALL SingleStepHook(jvmtiEnv* jvmtienv,
+ JNIEnv* env,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ ScopedThreadInfo info(jvmtienv, env, thread);
+ ScopedMethodInfo method_info(jvmtienv, env, method);
+ if (!method_info.Init()) {
+ LOG(ERROR) << "Unable to get method info!";
+ return;
+ }
+ LOG(INFO) << "Single step at location: 0x" << std::setw(8) << std::setfill('0') << std::hex
+ << location << " in method " << method_info << " thread: " << info.GetName();
+}
+
// The hook we are using.
void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
JNIEnv* jni_env ATTRIBUTE_UNUSED,
@@ -645,6 +662,8 @@ static void ReadOptions(StressData* data, char* options) {
std::string cur = GetOption(ops);
if (cur == "trace") {
data->trace_stress = true;
+ } else if (cur == "step") {
+ data->step_stress = true;
} else if (cur == "field") {
data->field_stress = true;
} else if (cur == "redefine") {
@@ -776,6 +795,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
cb.FieldAccess = FieldAccessHook;
cb.FieldModification = FieldModificationHook;
cb.ClassPrepare = ClassPrepareHook;
+ cb.SingleStep = SingleStepHook;
if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
LOG(ERROR) << "Unable to set class file load hook cb!";
return 1;
@@ -837,6 +857,13 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
return 1;
}
}
+ if (data->step_stress) {
+ if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ nullptr) != JVMTI_ERROR_NONE) {
+ return 1;
+ }
+ }
return 0;
}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index bf7692ab15..75694c340c 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -68,7 +68,7 @@ if $using_jack; then
fi
if [[ $mode == "host" ]]; then
- make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
+ make_command="make $j_arg $showcommands build-art-host-tests $common_targets dx-tests"
make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
elif [[ $mode == "target" ]]; then
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
index 78f73f5c11..58f6226ce7 100644
--- a/tools/dexfuzz/README
+++ b/tools/dexfuzz/README
@@ -137,9 +137,12 @@ FieldFlagChanger 40
InstructionDeleter 40
InstructionDuplicator 80
InstructionSwapper 80
+InvokeChanger 30
NewMethodCaller 10
NonsenseStringPrinter 10
+OppositeBranchChanger 40
PoolIndexChanger 30
+RandomBranchChanger 30
RandomInstructionGenerator 30
SwitchBranchShifter 30
TryBlockShifter 40
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
index 3b2875482e..97cdfee77e 100644
--- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -34,7 +34,7 @@ import dexfuzz.listeners.UpdatingConsoleListener;
*/
public class DexFuzz {
private static int majorVersion = 1;
- private static int minorVersion = 1;
+ private static int minorVersion = 3;
private static int seedChangeVersion = 0;
/**
diff --git a/tools/dexfuzz/src/dexfuzz/program/Program.java b/tools/dexfuzz/src/dexfuzz/program/Program.java
index 286fe5221e..1d0c678414 100644
--- a/tools/dexfuzz/src/dexfuzz/program/Program.java
+++ b/tools/dexfuzz/src/dexfuzz/program/Program.java
@@ -30,9 +30,12 @@ import dexfuzz.program.mutators.FieldFlagChanger;
import dexfuzz.program.mutators.InstructionDeleter;
import dexfuzz.program.mutators.InstructionDuplicator;
import dexfuzz.program.mutators.InstructionSwapper;
+import dexfuzz.program.mutators.InvokeChanger;
import dexfuzz.program.mutators.NewMethodCaller;
import dexfuzz.program.mutators.NonsenseStringPrinter;
+import dexfuzz.program.mutators.OppositeBranchChanger;
import dexfuzz.program.mutators.PoolIndexChanger;
+import dexfuzz.program.mutators.RandomBranchChanger;
import dexfuzz.program.mutators.RandomInstructionGenerator;
import dexfuzz.program.mutators.SwitchBranchShifter;
import dexfuzz.program.mutators.TryBlockShifter;
@@ -197,9 +200,12 @@ public class Program {
registerMutator(new InstructionDeleter(rng, mutationStats, mutations));
registerMutator(new InstructionDuplicator(rng, mutationStats, mutations));
registerMutator(new InstructionSwapper(rng, mutationStats, mutations));
+ registerMutator(new InvokeChanger(rng, mutationStats, mutations));
registerMutator(new NewMethodCaller(rng, mutationStats, mutations));
registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations));
+ registerMutator(new OppositeBranchChanger(rng, mutationStats, mutations));
registerMutator(new PoolIndexChanger(rng, mutationStats, mutations));
+ registerMutator(new RandomBranchChanger(rng, mutationStats, mutations));
registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations));
registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations));
registerMutator(new TryBlockShifter(rng, mutationStats, mutations));
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java
new file mode 100644
index 0000000000..872b29730c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * This class mutates the comparison operator of the if
+ * statements by taking in a random instruction, checking whether
+ * it is an if statement and, if so, changing the comparison
+ * operator. The inheriting classes implement the way comparison
+ * operator changes. For example, by choosing the opposite
+ * comparison operator or by choosing a random comparison operator.
+ */
+public abstract class IfBranchChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int ifBranchInsnIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(ifBranchInsnIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ ifBranchInsnIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public IfBranchChanger() { }
+
+ public IfBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> ifBranchInsns = null;
+
+ private void generateCachedifBranchInsns(MutatableCode mutatableCode) {
+ if (ifBranchInsns != null) {
+ return;
+ }
+
+ ifBranchInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isIfBranchOperation(mInsn)) {
+ ifBranchInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isIfBranchOperation(mInsn)) {
+ return true;
+ }
+ }
+
+ Log.debug("No if branch operation, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedifBranchInsns(mutatableCode);
+
+ int ifBranchInsnIdx = rng.nextInt(ifBranchInsns.size());
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.ifBranchInsnIdx = ifBranchInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedifBranchInsns(mutatableCode);
+
+ MInsn ifBranchInsn = ifBranchInsns.get(mutation.ifBranchInsnIdx);
+
+ String oldInsnString = ifBranchInsn.toString();
+
+ Opcode newOpcode = getModifiedOpcode(ifBranchInsn);
+
+ ifBranchInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
+
+ Log.info("Changed " + oldInsnString + " to " + ifBranchInsn);
+
+ stats.incrementStat("Changed if branch operator to " + getMutationTag() + " operator");
+
+ // Clear cache.
+ ifBranchInsns = null;
+ }
+
+ /**
+ * Get a different if branch instruction.
+ * @return opcode of the new comparison operator.
+ */
+ protected abstract Opcode getModifiedOpcode(MInsn mInsn);
+
+ /**
+ * Get the tag of the mutation that fired.
+ * @return string tag of the type of mutation used
+ */
+ protected abstract String getMutationTag();
+
+ private boolean isIfBranchOperation(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)) {
+ return true;
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java
new file mode 100644
index 0000000000..348850321c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class InvokeChanger extends CodeMutator {
+
+ private static final Opcode[] INVOKE_LIST = {
+ Opcode.INVOKE_VIRTUAL,
+ Opcode.INVOKE_SUPER,
+ Opcode.INVOKE_DIRECT,
+ Opcode.INVOKE_STATIC,
+ Opcode.INVOKE_INTERFACE,
+ };
+
+ private static final Opcode[] INVOKE_RANGE_LIST = {
+ Opcode.INVOKE_VIRTUAL_RANGE,
+ Opcode.INVOKE_SUPER_RANGE,
+ Opcode.INVOKE_DIRECT_RANGE,
+ Opcode.INVOKE_STATIC_RANGE,
+ Opcode.INVOKE_INTERFACE_RANGE,
+ };
+
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+
+ public int invokeCallInsnIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(invokeCallInsnIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ invokeCallInsnIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public InvokeChanger() { }
+
+ public InvokeChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> invokeCallInsns = null;
+
+ private void generateCachedinvokeCallInsns(MutatableCode mutatableCode) {
+ if (invokeCallInsns != null) {
+ return;
+ }
+
+ invokeCallInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isInvokeCallInst(mInsn)) {
+ invokeCallInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isInvokeCallInst(mInsn)) {
+ return true;
+ }
+ }
+
+ Log.debug("No invoke instruction in method, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedinvokeCallInsns(mutatableCode);
+
+ int invokeCallInsnIdx = rng.nextInt(invokeCallInsns.size());
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.invokeCallInsnIdx = invokeCallInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedinvokeCallInsns(mutatableCode);
+
+ MInsn invokeInsn = invokeCallInsns.get(mutation.invokeCallInsnIdx);
+
+ String oldInsnString = invokeInsn.toString();
+
+ Opcode newOpcode = isInvokeCalIInst(invokeInsn);
+
+ invokeInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
+
+ Log.info("Changed " + oldInsnString + " to " + invokeInsn);
+
+ stats.incrementStat("Changed invoke call instruction");
+
+ // Clear cache.
+ invokeCallInsns = null;
+ }
+
+ private Opcode isInvokeCalIInst(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (isSimpleInvokeInst(opcode)) {
+ int index = opcode.ordinal() - Opcode.INVOKE_VIRTUAL.ordinal();
+ int length = INVOKE_LIST.length;
+ return INVOKE_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
+ } else if (isRangeInvokeInst(opcode)) {
+ int index = opcode.ordinal() - Opcode.INVOKE_VIRTUAL_RANGE.ordinal();
+ int length = INVOKE_RANGE_LIST.length;
+ return INVOKE_RANGE_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
+ }
+ return opcode;
+ }
+
+ private boolean isSimpleInvokeInst(Opcode opcode){
+ return Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE);
+ }
+
+ private boolean isRangeInvokeInst(Opcode opcode){
+ return Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE);
+
+ }
+
+ private boolean isInvokeCallInst(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ return isSimpleInvokeInst(opcode) || isRangeInvokeInst(opcode);
+ }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java
new file mode 100644
index 0000000000..cb25b641cc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+import java.util.List;
+import java.util.Random;
+
+public class OppositeBranchChanger extends IfBranchChanger {
+
+ public OppositeBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 40;
+ }
+
+ @Override
+ protected Opcode getModifiedOpcode(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ switch (opcode) {
+ case IF_EQ:
+ return Opcode.IF_NE;
+ case IF_NE:
+ return Opcode.IF_EQ;
+ case IF_LT:
+ return Opcode.IF_GE;
+ case IF_GT:
+ return Opcode.IF_LE;
+ case IF_GE:
+ return Opcode.IF_LT;
+ case IF_LE:
+ return Opcode.IF_GT;
+ case IF_EQZ:
+ return Opcode.IF_NEZ;
+ case IF_NEZ:
+ return Opcode.IF_EQZ;
+ case IF_LTZ:
+ return Opcode.IF_GEZ;
+ case IF_GTZ:
+ return Opcode.IF_LEZ;
+ case IF_GEZ:
+ return Opcode.IF_LTZ;
+ case IF_LEZ:
+ return Opcode.IF_GTZ;
+ default:
+ Log.errorAndQuit("Could not find if branch.");
+ return opcode;
+ }
+ }
+
+ @Override
+ protected String getMutationTag() {
+ return "opposite";
+ }
+} \ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java
new file mode 100644
index 0000000000..fc42c2ed6f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+import java.util.List;
+import java.util.Random;
+
+public class RandomBranchChanger extends IfBranchChanger {
+
+ private static final Opcode[] EQUALITY_CMP_OP_LIST = {
+ Opcode.IF_EQ,
+ Opcode.IF_NE,
+ Opcode.IF_LT,
+ Opcode.IF_GE,
+ Opcode.IF_GT,
+ Opcode.IF_LE
+ };
+
+ private static final Opcode[] ZERO_CMP_OP_LIST = {
+ Opcode.IF_EQZ,
+ Opcode.IF_NEZ,
+ Opcode.IF_LTZ,
+ Opcode.IF_GEZ,
+ Opcode.IF_GTZ,
+ Opcode.IF_LEZ
+ };
+
+ public RandomBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ @Override
+ protected Opcode getModifiedOpcode(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LE)) {
+ int index = opcode.ordinal() - Opcode.IF_EQ.ordinal();
+ int length = EQUALITY_CMP_OP_LIST.length;
+ return EQUALITY_CMP_OP_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
+ } else if (Opcode.isBetween(opcode, Opcode.IF_EQZ, Opcode.IF_LEZ)) {
+ int index = opcode.ordinal() - Opcode.IF_EQZ.ordinal();
+ int length = ZERO_CMP_OP_LIST.length;
+ return ZERO_CMP_OP_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
+ }
+ return opcode;
+ }
+
+ @Override
+ protected String getMutationTag() {
+ return "random";
+ }
+} \ No newline at end of file
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
new file mode 100644
index 0000000000..e049cb3ee9
--- /dev/null
+++ b/tools/libcore_gcstress_failures.txt
@@ -0,0 +1,13 @@
+/*
+ * This file contains expectations for ART's buildbot when running gcstress.
+ * The script that uses this file is art/tools/run-libcore-tests.sh.
+ */
+
+[
+{
+ description: "Timeouts on target with gcstress.",
+ result: EXEC_FAILED,
+ modes: [device],
+ names: ["libcore.javax.crypto.CipherBasicsTest#testGcmEncryption"]
+}
+]
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 6dcc23a9fc..eef74d27ab 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -158,9 +158,12 @@ if $use_jit; then
fi
vogar_args="$vogar_args --vm-arg -Xusejit:$use_jit"
-# gcstress and debug may lead to timeouts, so we need a dedicated expectations file for it.
-if [[ $gcstress && $debug ]]; then
- expectations="$expectations --expectations art/tools/libcore_gcstress_debug_failures.txt"
+# gcstress may lead to timeouts, so we need dedicated expectations files for it.
+if [[ $gcstress ]]; then
+ expectations="$expectations --expectations art/tools/libcore_gcstress_failures.txt"
+ if [[ $debug ]]; then
+ expectations="$expectations --expectations art/tools/libcore_gcstress_debug_failures.txt"
+ fi
fi
# Run the tests using vogar.