summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk65
-rw-r--r--build/Android.common_build.mk36
-rw-r--r--build/Android.common_path.mk4
-rw-r--r--build/Android.common_test.mk56
-rw-r--r--build/Android.gtest.mk2
-rw-r--r--build/Android.oat.mk4
-rw-r--r--compiler/Android.mk3
-rw-r--r--compiler/dex/bb_optimizations.h10
-rw-r--r--compiler/dex/dataflow_iterator-inl.h50
-rw-r--r--compiler/dex/dataflow_iterator.h46
-rw-r--r--compiler/dex/frontend.cc12
-rw-r--r--compiler/dex/global_value_numbering.cc88
-rw-r--r--compiler/dex/global_value_numbering.h18
-rw-r--r--compiler/dex/global_value_numbering_test.cc67
-rw-r--r--compiler/dex/local_value_numbering.cc31
-rw-r--r--compiler/dex/mir_graph.cc266
-rw-r--r--compiler/dex/mir_graph.h27
-rw-r--r--compiler/dex/mir_graph_test.cc381
-rw-r--r--compiler/dex/mir_optimization.cc21
-rw-r--r--compiler/dex/mir_optimization_test.cc41
-rw-r--r--compiler/dex/pass_driver_me.h3
-rw-r--r--compiler/dex/pass_me.h1
-rw-r--r--compiler/dex/quick/arm/call_arm.cc4
-rw-r--r--compiler/dex/quick/arm/codegen_arm.h5
-rw-r--r--compiler/dex/quick/arm/fp_arm.cc54
-rw-r--r--compiler/dex/quick/arm/int_arm.cc24
-rw-r--r--compiler/dex/quick/arm/utility_arm.cc2
-rw-r--r--compiler/dex/quick/arm64/call_arm64.cc4
-rw-r--r--compiler/dex/quick/arm64/codegen_arm64.h10
-rw-r--r--compiler/dex/quick/arm64/fp_arm64.cc55
-rw-r--r--compiler/dex/quick/arm64/int_arm64.cc212
-rw-r--r--compiler/dex/quick/gen_common.cc120
-rwxr-xr-xcompiler/dex/quick/gen_invoke.cc46
-rw-r--r--compiler/dex/quick/mips/codegen_mips.h5
-rw-r--r--compiler/dex/quick/mips/int_mips.cc22
-rw-r--r--compiler/dex/quick/mir_to_lir.h79
-rw-r--r--compiler/dex/quick/ralloc_util.cc61
-rw-r--r--compiler/dex/quick/x86/assemble_x86.cc4
-rw-r--r--compiler/dex/quick/x86/codegen_x86.h20
-rwxr-xr-xcompiler/dex/quick/x86/fp_x86.cc73
-rwxr-xr-xcompiler/dex/quick/x86/int_x86.cc165
-rwxr-xr-xcompiler/dex/quick/x86/target_x86.cc186
-rw-r--r--compiler/dex/quick/x86/utility_x86.cc1
-rw-r--r--compiler/dex/quick/x86/x86_lir.h12
-rw-r--r--compiler/dex/quick_compiler_callbacks.h5
-rw-r--r--compiler/dex/vreg_analysis.cc6
-rw-r--r--compiler/driver/compiler_driver-inl.h4
-rw-r--r--compiler/driver/compiler_driver.cc31
-rw-r--r--compiler/elf_patcher.cc293
-rw-r--r--compiler/elf_patcher.h132
-rw-r--r--compiler/image_test.cc2
-rw-r--r--compiler/image_writer.cc226
-rw-r--r--compiler/image_writer.h8
-rw-r--r--compiler/oat_test.cc3
-rw-r--r--compiler/oat_writer.cc5
-rw-r--r--compiler/oat_writer.h2
-rw-r--r--compiler/optimizing/builder.cc68
-rw-r--r--compiler/optimizing/code_generator.cc5
-rw-r--r--compiler/optimizing/code_generator.h15
-rw-r--r--compiler/optimizing/code_generator_arm.cc184
-rw-r--r--compiler/optimizing/code_generator_arm.h10
-rw-r--r--compiler/optimizing/code_generator_x86.cc100
-rw-r--r--compiler/optimizing/code_generator_x86.h6
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc175
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/codegen_test.cc23
-rw-r--r--compiler/optimizing/graph_visualizer.cc4
-rw-r--r--compiler/optimizing/locations.cc7
-rw-r--r--compiler/optimizing/locations.h46
-rw-r--r--compiler/optimizing/nodes.h28
-rw-r--r--compiler/optimizing/optimizing_compiler.cc9
-rw-r--r--compiler/optimizing/register_allocator.cc24
-rw-r--r--compiler/optimizing/register_allocator.h5
-rw-r--r--compiler/utils/arm/assembler_arm.cc9
-rw-r--r--compiler/utils/arm/assembler_arm.h4
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc47
-rw-r--r--compiler/utils/arm/assembler_thumb2.h17
-rw-r--r--compiler/utils/assembler_thumb_test.cc19
-rw-r--r--compiler/utils/assembler_thumb_test_expected.cc.inc4
-rw-r--r--compiler/utils/stack_checks.h44
-rw-r--r--compiler/utils/x86/assembler_x86.cc7
-rw-r--r--compiler/utils/x86/assembler_x86.h1
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc65
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h10
-rw-r--r--dex2oat/dex2oat.cc39
-rw-r--r--oatdump/oatdump.cc15
-rw-r--r--patchoat/patchoat.cc109
-rw-r--r--patchoat/patchoat.h1
-rw-r--r--runtime/Android.mk2
-rw-r--r--runtime/arch/arm/fault_handler_arm.cc4
-rw-r--r--runtime/arch/memcmp16.h2
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S7
-rw-r--r--runtime/arch/x86/asm_support_x86.S4
-rw-r--r--runtime/arch/x86/memcmp16_x86.S1038
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S12
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S4
-rw-r--r--runtime/base/mutex.cc32
-rw-r--r--runtime/base/mutex.h14
-rw-r--r--runtime/base/scoped_flock.cc16
-rw-r--r--runtime/base/scoped_flock.h5
-rw-r--r--runtime/class_linker.cc449
-rw-r--r--runtime/class_linker.h40
-rw-r--r--runtime/class_linker_test.cc6
-rw-r--r--runtime/common_runtime_test.cc27
-rw-r--r--runtime/common_runtime_test.h2
-rw-r--r--runtime/compiler_callbacks.h4
-rw-r--r--runtime/debugger.cc24
-rw-r--r--runtime/dex_file_verifier.cc67
-rw-r--r--runtime/dex_file_verifier.h3
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h25
-rw-r--r--runtime/entrypoints/entrypoint_utils.cc97
-rw-r--r--runtime/entrypoints/entrypoint_utils.h6
-rw-r--r--runtime/gc/collector/semi_space.cc9
-rw-r--r--runtime/gc/heap.cc299
-rw-r--r--runtime/gc/heap.h13
-rw-r--r--runtime/gc/space/bump_pointer_space.cc8
-rw-r--r--runtime/gc/space/bump_pointer_space.h3
-rw-r--r--runtime/gc/space/image_space.cc321
-rw-r--r--runtime/gc/space/image_space.h26
-rw-r--r--runtime/gc/space/large_object_space.cc5
-rw-r--r--runtime/gc/space/large_object_space.h3
-rw-r--r--runtime/gc/space/malloc_space.h2
-rw-r--r--runtime/gc/space/space.h2
-rw-r--r--runtime/gc/space/zygote_space.cc13
-rw-r--r--runtime/gc/space/zygote_space.h3
-rw-r--r--runtime/hprof/hprof.cc34
-rw-r--r--runtime/indirect_reference_table.cc1
-rw-r--r--runtime/instruction_set.cc38
-rw-r--r--runtime/instruction_set.h28
-rw-r--r--runtime/instrumentation.cc102
-rw-r--r--runtime/instrumentation.h27
-rw-r--r--runtime/interpreter/interpreter.cc9
-rw-r--r--runtime/interpreter/interpreter_common.h3
-rw-r--r--runtime/jdwp/jdwp.h2
-rw-r--r--runtime/jdwp/jdwp_handler.cc4
-rw-r--r--runtime/jdwp/jdwp_main.cc2
-rw-r--r--runtime/mirror/art_method.cc5
-rw-r--r--runtime/mirror/class-inl.h50
-rw-r--r--runtime/mirror/class.cc53
-rw-r--r--runtime/mirror/class.h18
-rw-r--r--runtime/mirror/object.cc6
-rw-r--r--runtime/mirror/object.h9
-rw-r--r--runtime/monitor.cc6
-rw-r--r--runtime/native/dalvik_system_VMStack.cc7
-rw-r--r--runtime/native/java_lang_Class.cc5
-rw-r--r--runtime/native/java_lang_Thread.cc9
-rw-r--r--runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc7
-rw-r--r--runtime/noop_compiler_callbacks.h5
-rw-r--r--runtime/oat.cc31
-rw-r--r--runtime/oat.h7
-rw-r--r--runtime/oat_file.cc5
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/parsed_options.cc12
-rw-r--r--runtime/parsed_options.h4
-rw-r--r--runtime/reference_table_test.cc2
-rw-r--r--runtime/runtime.cc15
-rw-r--r--runtime/runtime.h20
-rw-r--r--runtime/thread-inl.h30
-rw-r--r--runtime/thread.cc12
-rw-r--r--runtime/thread.h15
-rw-r--r--runtime/thread_list.cc29
-rw-r--r--runtime/thread_list.h2
-rw-r--r--runtime/utils.cc59
-rw-r--r--runtime/utils.h13
-rw-r--r--runtime/verifier/method_verifier.cc57
-rw-r--r--runtime/well_known_classes.cc4
-rw-r--r--runtime/well_known_classes.h2
-rw-r--r--test/000-nop/build (renamed from test/001-nop/build)0
-rw-r--r--test/000-nop/expected.txt (renamed from test/001-nop/expected.txt)0
-rw-r--r--test/000-nop/info.txt (renamed from test/001-nop/info.txt)0
-rw-r--r--test/000-nop/run (renamed from test/001-nop/run)0
-rw-r--r--test/001-HelloWorld/expected.txt1
-rw-r--r--test/001-HelloWorld/info.txt1
-rw-r--r--test/001-HelloWorld/src/Main.java (renamed from test/HelloWorld/HelloWorld.java)4
-rw-r--r--test/001-Main/expected.txt0
-rw-r--r--test/001-Main/info.txt1
-rw-r--r--test/001-Main/src/Main.java (renamed from test/009-instanceof2/src/Iface2.java)11
-rw-r--r--test/004-InterfaceTest/expected.txt2
-rw-r--r--test/004-InterfaceTest/info.txt1
-rw-r--r--test/004-InterfaceTest/src/Main.java (renamed from test/InterfaceTest/InterfaceTest.java)6
-rw-r--r--test/004-JniTest/expected.txt0
-rw-r--r--test/004-JniTest/info.txt1
-rw-r--r--test/004-JniTest/jni_test.cc (renamed from test/JniTest/jni_test.cc)32
-rw-r--r--test/004-JniTest/src/Main.java (renamed from test/JniTest/JniTest.java)2
-rw-r--r--test/004-NativeAllocations/expected.txt1
-rw-r--r--test/004-NativeAllocations/info.txt1
-rw-r--r--test/004-NativeAllocations/src/Main.java (renamed from test/NativeAllocations/NativeAllocations.java)2
-rw-r--r--test/004-ReferenceMap/expected.txt0
-rw-r--r--test/004-ReferenceMap/info.txt1
-rw-r--r--test/004-ReferenceMap/src/Main.java (renamed from test/ReferenceMap/ReferenceMap.java)6
-rw-r--r--test/004-ReferenceMap/stack_walk_refmap_jni.cc (renamed from test/ReferenceMap/stack_walk_refmap_jni.cc)2
-rw-r--r--test/004-SignalTest/expected.txt5
-rw-r--r--test/004-SignalTest/info.txt1
-rw-r--r--test/004-SignalTest/signaltest.cc (renamed from test/SignalTest/signaltest.cc)6
-rw-r--r--test/004-SignalTest/src/Main.java (renamed from test/SignalTest/SignalTest.java)2
-rw-r--r--test/004-StackWalk/expected.txt4
-rw-r--r--test/004-StackWalk/info.txt1
-rw-r--r--test/004-StackWalk/src/Main.java (renamed from test/StackWalk/StackWalk.java)10
-rw-r--r--test/004-StackWalk/stack_walk_jni.cc (renamed from test/StackWalk/stack_walk_jni.cc)5
-rwxr-xr-xtest/004-ThreadStress/check18
-rw-r--r--test/004-ThreadStress/expected.txt1
-rw-r--r--test/004-ThreadStress/info.txt1
-rw-r--r--test/004-ThreadStress/src/Main.java (renamed from test/ThreadStress/ThreadStress.java)35
-rw-r--r--test/004-UnsafeTest/expected.txt0
-rw-r--r--test/004-UnsafeTest/info.txt1
-rw-r--r--test/004-UnsafeTest/src/Main.java (renamed from test/UnsafeTest/UnsafeTest.java)6
-rw-r--r--test/004-UnsafeTest/unsafe_test.cc (renamed from test/UnsafeTest/unsafe_test.cc)4
-rw-r--r--test/005-annotations/build (renamed from test/004-annotations/build)0
-rw-r--r--test/005-annotations/expected.txt (renamed from test/004-annotations/expected.txt)0
-rw-r--r--test/005-annotations/info.txt (renamed from test/004-annotations/info.txt)0
-rw-r--r--test/005-annotations/src/Main.java (renamed from test/004-annotations/src/Main.java)0
-rw-r--r--test/005-annotations/src/android/test/AnnoSimplePackage1.java (renamed from test/004-annotations/src/android/test/AnnoSimplePackage1.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoArrayField.java (renamed from test/004-annotations/src/android/test/anno/AnnoArrayField.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoFancyConstructor.java (renamed from test/004-annotations/src/android/test/anno/AnnoFancyConstructor.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoFancyField.java (renamed from test/004-annotations/src/android/test/anno/AnnoFancyField.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoFancyMethod.java (renamed from test/004-annotations/src/android/test/anno/AnnoFancyMethod.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoFancyParameter.java (renamed from test/004-annotations/src/android/test/anno/AnnoFancyParameter.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoFancyType.java (renamed from test/004-annotations/src/android/test/anno/AnnoFancyType.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleConstructor.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleField.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleField.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleMethod.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleMethod.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimplePackage.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimplePackage.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleParameter.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleParameter.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleType.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleType.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleType2.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleType2.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java (renamed from test/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/ExportedProperty.java (renamed from test/004-annotations/src/android/test/anno/ExportedProperty.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/FullyNoted.java (renamed from test/004-annotations/src/android/test/anno/FullyNoted.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/INoted.java (renamed from test/004-annotations/src/android/test/anno/INoted.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/IntToString.java (renamed from test/004-annotations/src/android/test/anno/IntToString.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/MissingAnnotation.java (renamed from test/004-annotations/src/android/test/anno/MissingAnnotation.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/SimplyNoted.java (renamed from test/004-annotations/src/android/test/anno/SimplyNoted.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/SomeClass.java (renamed from test/004-annotations/src/android/test/anno/SomeClass.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/SubNoted.java (renamed from test/004-annotations/src/android/test/anno/SubNoted.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/TestAnnotations.java (renamed from test/004-annotations/src/android/test/anno/TestAnnotations.java)0
-rw-r--r--test/005-annotations/src/android/test/anno/package-info.java (renamed from test/004-annotations/src/android/test/anno/package-info.java)0
-rw-r--r--test/005-annotations/src/android/test/package-info.java (renamed from test/004-annotations/src/android/test/package-info.java)0
-rw-r--r--test/006-args/expected.txt (renamed from test/005-args/expected.txt)0
-rw-r--r--test/006-args/info.txt (renamed from test/005-args/info.txt)0
-rw-r--r--test/006-args/src/ArgsTest.java (renamed from test/005-args/src/ArgsTest.java)0
-rw-r--r--test/006-args/src/Main.java (renamed from test/005-args/src/Main.java)0
-rw-r--r--test/007-count10/expected.txt (renamed from test/006-count10/expected.txt)0
-rw-r--r--test/007-count10/info.txt (renamed from test/006-count10/info.txt)0
-rw-r--r--test/007-count10/src/Main.java (renamed from test/006-count10/src/Main.java)0
-rw-r--r--test/008-exceptions/expected.txt (renamed from test/007-exceptions/expected.txt)0
-rw-r--r--test/008-exceptions/info.txt (renamed from test/007-exceptions/info.txt)0
-rw-r--r--test/008-exceptions/src/Main.java (renamed from test/007-exceptions/src/Main.java)0
-rw-r--r--test/008-instanceof/src/Main.java49
-rw-r--r--test/009-instanceof/expected.txt (renamed from test/008-instanceof/expected.txt)5
-rw-r--r--test/009-instanceof/info.txt (renamed from test/008-instanceof/info.txt)0
-rw-r--r--test/009-instanceof/src/Iface1.java (renamed from test/008-instanceof/src/Iface1.java)0
-rw-r--r--test/009-instanceof/src/Iface2.java (renamed from test/008-instanceof/src/Iface2.java)0
-rw-r--r--test/009-instanceof/src/Iface2Sub1.java (renamed from test/008-instanceof/src/Iface2Sub1.java)0
-rw-r--r--test/009-instanceof/src/ImplA.java (renamed from test/008-instanceof/src/ImplA.java)0
-rw-r--r--test/009-instanceof/src/ImplB.java (renamed from test/008-instanceof/src/ImplB.java)0
-rw-r--r--test/009-instanceof/src/ImplBSub.java (renamed from test/008-instanceof/src/ImplBSub.java)0
-rw-r--r--test/009-instanceof/src/Main.java (renamed from test/009-instanceof2/src/Main.java)28
-rw-r--r--test/009-instanceof2/expected.txt5
-rw-r--r--test/009-instanceof2/info.txt6
-rw-r--r--test/009-instanceof2/src/Iface1.java27
-rw-r--r--test/009-instanceof2/src/Iface2Sub1.java23
-rw-r--r--test/009-instanceof2/src/ImplA.java28
-rw-r--r--test/009-instanceof2/src/ImplB.java30
-rw-r--r--test/009-instanceof2/src/ImplBSub.java28
-rw-r--r--test/018-stack-overflow/expected.txt9
-rwxr-xr-xtest/018-stack-overflow/run23
-rw-r--r--test/018-stack-overflow/src/Main.java30
-rw-r--r--test/050-sync-test/src/Main.java18
-rw-r--r--test/080-oom-throw/src/Main.java4
-rw-r--r--test/082-inline-execute/src/Main.java155
-rw-r--r--test/083-compiler-regressions/expected.txt1
-rw-r--r--test/083-compiler-regressions/src/Main.java23
-rw-r--r--test/100-reflect2/expected.txt2
-rw-r--r--test/114-ParallelGC/expected.txt0
-rw-r--r--test/114-ParallelGC/info.txt1
-rw-r--r--test/114-ParallelGC/src/Main.java (renamed from test/ParallelGC/ParallelGC.java)9
-rw-r--r--test/401-optimizing-compiler/src/Main.java37
-rw-r--r--test/700-LoadArgRegs/expected.txt1
-rw-r--r--test/700-LoadArgRegs/src/Main.java11
-rw-r--r--test/Android.libarttest.mk10
-rw-r--r--test/Android.oat.mk456
-rw-r--r--test/Android.run-test.mk646
-rwxr-xr-xtest/etc/default-check17
-rwxr-xr-xtest/etc/host-run-test-jar53
-rwxr-xr-xtest/etc/push-and-run-prebuilt-test-jar211
-rwxr-xr-xtest/etc/push-and-run-test-jar16
-rwxr-xr-xtest/run-all-tests15
-rwxr-xr-xtest/run-test84
289 files changed, 7187 insertions, 2614 deletions
diff --git a/Android.mk b/Android.mk
index 7a95dfec47..c5e90f26c8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -127,7 +127,6 @@ TEST_ART_TARGET_SYNC_DEPS :=
include $(art_path)/build/Android.common_test.mk
include $(art_path)/build/Android.gtest.mk
-include $(art_path)/test/Android.oat.mk
include $(art_path)/test/Android.run-test.mk
# Sync test files to the target, depends upon all things that must be pushed to the target.
@@ -169,65 +168,59 @@ test-art-host-vixl: $(VIXL_TEST_DEPENDENCY)
# "mm test-art-host" to build and run all host tests.
.PHONY: test-art-host
-test-art-host: test-art-host-gtest test-art-host-oat test-art-host-run-test test-art-host-vixl
+test-art-host: test-art-host-gtest test-art-host-run-test test-art-host-vixl
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely with the default compiler.
.PHONY: test-art-host-default
-test-art-host-default: test-art-host-oat-default test-art-host-run-test-default
+test-art-host-default: test-art-host-run-test-default
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely with the optimizing compiler.
.PHONY: test-art-host-optimizing
-test-art-host-optimizing: test-art-host-oat-optimizing test-art-host-run-test-optimizing
+test-art-host-optimizing: test-art-host-run-test-optimizing
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All host tests that run solely on the interpreter.
.PHONY: test-art-host-interpreter
-test-art-host-interpreter: test-art-host-oat-interpreter test-art-host-run-test-interpreter
+test-art-host-interpreter: test-art-host-run-test-interpreter
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Primary host architecture variants:
.PHONY: test-art-host$(ART_PHONY_TEST_HOST_SUFFIX)
test-art-host$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-gtest$(ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-oat$(ART_PHONY_TEST_HOST_SUFFIX) test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX)
+ test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX)
-test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-default$(ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX)
+test-art-host-default$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX)
-test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-optimizing$(ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX)
+test-art-host-optimizing$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX)
-test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-interpreter$(ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX)
+test-art-host-interpreter$(ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-interpreter$(ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Secondary host architecture variants:
ifneq ($(HOST_PREFER_32_BIT),true)
.PHONY: test-art-host$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
test-art-host$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-gtest$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-oat$(2ND_ART_PHONY_TEST_HOST_SUFFIX) test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+ test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
-test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+test-art-host-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-default$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
-test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+test-art-host-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
-test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-oat-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX) \
- test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
+test-art-host-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX): test-art-host-run-test-interpreter$(2ND_ART_PHONY_TEST_HOST_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
@@ -236,65 +229,59 @@ endif
# "mm test-art-target" to build and run all target tests.
.PHONY: test-art-target
-test-art-target: test-art-target-gtest test-art-target-oat test-art-target-run-test
+test-art-target: test-art-target-gtest test-art-target-run-test
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely with the default compiler.
.PHONY: test-art-target-default
-test-art-target-default: test-art-target-oat-default test-art-target-run-test-default
+test-art-target-default: test-art-target-run-test-default
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely with the optimizing compiler.
.PHONY: test-art-target-optimizing
-test-art-target-optimizing: test-art-target-oat-optimizing test-art-target-run-test-optimizing
+test-art-target-optimizing: test-art-target-run-test-optimizing
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# All target tests that run solely on the interpreter.
.PHONY: test-art-target-interpreter
-test-art-target-interpreter: test-art-target-oat-interpreter test-art-target-run-test-interpreter
+test-art-target-interpreter: test-art-target-run-test-interpreter
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Primary target architecture variants:
.PHONY: test-art-target$(ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-oat$(ART_PHONY_TEST_TARGET_SUFFIX) test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX)
+ test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX)
-test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-default$(ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX)
+test-art-target-default$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX)
-test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX)
+test-art-target-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX)
-test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX)
+test-art-target-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-interpreter$(ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
# Secondary target architecture variants:
ifdef TARGET_2ND_ARCH
.PHONY: test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
test-art-target$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-gtest$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-oat$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+ test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
-test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+test-art-target-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-default$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
-test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+test-art-target-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
.PHONY: test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
-test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-oat-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX) \
- test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
+test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target-run-test-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
@@ -322,14 +309,14 @@ else
.PHONY: oat-target-$(1)
oat-target-$(1): $$(OUT_OAT_FILE)
-$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OATD_DEPENDENCY)
+$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OATD)
@mkdir -p $$(dir $$@)
$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
--boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --dex-file=$(PRODUCT_OUT)/$(1) \
--dex-location=/$(1) --oat-file=$$@ \
--instruction-set=$(DEX2OAT_TARGET_ARCH) \
--instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
- --android-root=$(PRODUCT_OUT)/system
+ --android-root=$(PRODUCT_OUT)/system --include-patch-information
endif
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index c39bc5dd63..207dc55224 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -183,8 +183,16 @@ endif
art_non_debug_cflags := \
-O3
+art_host_non_debug_cflags := \
+ $(art_non_debug_cflags)
+
+art_target_non_debug_cflags := \
+ $(art_non_debug_cflags)
+
ifeq ($(HOST_OS),linux)
- art_non_debug_cflags += -Wframe-larger-than=1728
+ # Larger frame-size for host clang builds today
+ art_host_non_debug_cflags += -Wframe-larger-than=2600
+ art_target_non_debug_cflags += -Wframe-larger-than=1728
endif
# FIXME: upstream LLVM has a vectorizer bug that needs to be fixed
@@ -207,6 +215,25 @@ ifndef LIBART_IMG_TARGET_BASE_ADDRESS
$(error LIBART_IMG_TARGET_BASE_ADDRESS unset)
endif
ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
+
+ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA
+ LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA=-0x1000000
+endif
+ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA
+ LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA=0x1000000
+endif
+ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
+ART_HOST_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
+
+ifndef LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA
+ LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA=-0x1000000
+endif
+ifndef LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA
+ LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA=0x1000000
+endif
+ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA)
+ART_TARGET_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA)
+
ART_TARGET_LDFLAGS :=
ifeq ($(TARGET_CPU_SMP),true)
ART_TARGET_CFLAGS += -DANDROID_SMP=1
@@ -266,8 +293,8 @@ ifeq ($(TARGET_ARCH),arm)
endif
endif
-ART_HOST_NON_DEBUG_CFLAGS := $(art_non_debug_cflags)
-ART_TARGET_NON_DEBUG_CFLAGS := $(art_non_debug_cflags)
+ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags)
+ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags)
# TODO: move -fkeep-inline-functions to art_debug_cflags when target gcc > 4.4 (and -lsupc++)
ART_HOST_DEBUG_CFLAGS := $(art_debug_cflags) -fkeep-inline-functions
@@ -329,5 +356,8 @@ endif
ART_DEFAULT_GC_TYPE :=
ART_DEFAULT_GC_TYPE_CFLAGS :=
art_cflags :=
+art_target_non_debug_cflags :=
+art_host_non_debug_cflags :=
+art_non_debug_cflags :=
endif # ANDROID_COMMON_BUILD_MK
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 8c0d9f29e8..bd1e8aa6a4 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -31,7 +31,11 @@ ART_TARGET_TEST_DIR := /data/art-test
ART_TARGET_TEST_OUT := $(TARGET_OUT_DATA)/art-test
# Directory used for temporary test files on the host.
+ifneq ($(TMPDIR),)
+ART_HOST_TEST_DIR := $(TMPDIR)/test-art-$(shell echo $$PPID)
+else
ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo $$PPID)
+endif
# Core.oat location on the device.
TARGET_CORE_OAT := $(ART_TARGET_TEST_DIR)/$(DEX2OAT_TARGET_ARCH)/core.oat
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index ed07129786..c50b4ae240 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -22,8 +22,26 @@ include art/build/Android.common_path.mk
# List of known broken tests that we won't attempt to execute. The test name must be the full
# rule name such as test-art-host-oat-optimizing-HelloWorld64.
ART_TEST_KNOWN_BROKEN := \
- test-art-host-oat-optimizing-SignalTest64 \
- test-art-host-oat-optimizing-SignalTest32
+ test-art-host-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
+ test-art-host-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
+ test-art-host-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
+ test-art-host-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
+ test-art-host-run-test-gcverify-optimizing-no-prebuild-004-SignalTest32 \
+ test-art-host-run-test-gcverify-optimizing-prebuild-004-SignalTest32 \
+ test-art-host-run-test-gcverify-optimizing-norelocate-004-SignalTest32 \
+ test-art-host-run-test-gcverify-optimizing-relocate-004-SignalTest32 \
+ test-art-host-run-test-optimizing-no-prebuild-004-SignalTest32 \
+ test-art-host-run-test-optimizing-prebuild-004-SignalTest32 \
+ test-art-host-run-test-optimizing-norelocate-004-SignalTest32 \
+ test-art-host-run-test-optimizing-relocate-004-SignalTest32 \
+ test-art-target-run-test-gcstress-optimizing-prebuild-004-SignalTest32 \
+ test-art-target-run-test-gcstress-optimizing-norelocate-004-SignalTest32 \
+ test-art-target-run-test-gcstress-default-prebuild-004-SignalTest32 \
+ test-art-target-run-test-gcstress-default-norelocate-004-SignalTest32 \
+ test-art-target-run-test-gcstress-optimizing-relocate-004-SignalTest32 \
+ test-art-target-run-test-gcstress-default-relocate-004-SignalTest32 \
+ test-art-target-run-test-gcstress-optimizing-no-prebuild-004-SignalTest32 \
+ test-art-target-run-test-gcstress-default-no-prebuild-004-SignalTest32
# List of known failing tests that when executed won't cause test execution to not finish.
# The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64.
@@ -32,13 +50,43 @@ ART_TEST_KNOWN_FAILING :=
# Keep going after encountering a test failure?
ART_TEST_KEEP_GOING ?= false
+# Do you want all tests, even those that are time consuming?
+ART_TEST_FULL ?= true
+
+# Do you want optimizing compiler tests run?
+ART_TEST_OPTIMIZING ?= $(ART_TEST_FULL)
+
+# Do you want tracing tests run?
+ART_TEST_TRACE ?= $(ART_TEST_FULL)
+
+# Do you want tests with GC verification enabled run?
+ART_TEST_GC_VERIFY ?= $(ART_TEST_FULL)
+
+# Do you want tests with the GC stress mode enabled run?
+ART_TEST_GC_STRESS ?= $(ART_TEST_FULL)
+
+# Do you want oat tests with relocation enabled?
+ART_TEST_OAT_RELOCATE ?= true
+
+# Do you want oat tests with relocation disabled?
+ART_TEST_OAT_NO_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation enabled?
+ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with relocation disabled?
+ART_TEST_RUN_TEST_NO_RELOCATE ?= $(ART_TEST_FULL)
+
+# Do you want run-tests with prebuild disabled?
+ART_TEST_RUN_TEST_NO_PREBUILD ?= $(ART_TEST_FULL)
+
# Define the command run on test failure. $(1) is the name of the test. Executed by the shell.
define ART_TEST_FAILED
( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \
(mkdir -p $(ART_HOST_TEST_DIR)/failed/ && touch $(ART_HOST_TEST_DIR)/failed/$(1) && \
echo $(ART_TEST_KNOWN_FAILING) | grep -q $(1) \
&& (echo -e "$(1) \e[91mKNOWN FAILURE\e[0m") \
- || (echo -e "$(1) \e[91mFAILED\e[0m")))
+ || (echo -e "$(1) \e[91mFAILED\e[0m" >&2 )))
endef
# Define the command run on test success. $(1) is the name of the test. Executed by the shell.
@@ -64,7 +112,7 @@ define ART_TEST_PREREQ_FINISHED
&& (echo -e "\e[93mSKIPPED TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/skipped/) \
|| (echo -e "\e[92mNO TESTS SKIPPED\e[0m")) && \
([ -d $(ART_HOST_TEST_DIR)/failed/ ] \
- && (echo -e "\e[91mFAILING TESTS\e[0m" && ls -1 $(ART_HOST_TEST_DIR)/failed/) \
+ && (echo -e "\e[91mFAILING TESTS\e[0m" >&2 && ls -1 $(ART_HOST_TEST_DIR)/failed/ >&2) \
|| (echo -e "\e[92mNO TESTS FAILED\e[0m")) \
&& ([ ! -d $(ART_HOST_TEST_DIR)/failed/ ] && rm -r $(ART_HOST_TEST_DIR) \
|| (rm -r $(ART_HOST_TEST_DIR) && false)))))
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index ee51fcd92e..2cba0ec9ce 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -127,6 +127,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \
runtime/reflection_test.cc \
compiler/dex/global_value_numbering_test.cc \
compiler/dex/local_value_numbering_test.cc \
+ compiler/dex/mir_graph_test.cc \
compiler/dex/mir_optimization_test.cc \
compiler/driver/compiler_driver_test.cc \
compiler/elf_writer_test.cc \
@@ -171,6 +172,7 @@ COMPILER_GTEST_TARGET_SRC_FILES := \
COMPILER_GTEST_HOST_SRC_FILES := \
$(COMPILER_GTEST_COMMON_SRC_FILES) \
+ compiler/utils//assembler_thumb_test.cc \
compiler/utils/x86/assembler_x86_test.cc \
compiler/utils/x86_64/assembler_x86_64_test.cc
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 10936a45d6..cd6b13aac3 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -26,7 +26,7 @@ include art/build/Android.common_path.mk
# Use dex2oat debug version for better error reporting
# $(1): 2ND_ or undefined, 2ND_ for 32-bit host builds.
define create-core-oat-host-rules
-$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY)
+$$($(1)HOST_CORE_IMG_OUT): $$(HOST_CORE_DEX_FILES) $$(DEX2OATD)
@echo "host dex2oat: $$@ ($$?)"
@mkdir -p $$(dir $$@)
$$(hide) $$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
@@ -49,7 +49,7 @@ $(eval $(call create-core-oat-host-rules,2ND_))
endif
define create-core-oat-target-rules
-$$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD_DEPENDENCY)
+$$($(1)TARGET_CORE_IMG_OUT): $$($(1)TARGET_CORE_DEX_FILES) $$(DEX2OATD)
@echo "target dex2oat: $$@ ($$?)"
@mkdir -p $$(dir $$@)
$$(hide) $$(DEX2OATD) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) \
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 02dad2ae36..61ff109d4e 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -95,8 +95,8 @@ LIBART_COMPILER_SRC_FILES := \
optimizing/register_allocator.cc \
optimizing/ssa_builder.cc \
optimizing/ssa_liveness_analysis.cc \
- optimizing/ssa_type_propagation.cc \
optimizing/ssa_phi_elimination.cc \
+ optimizing/ssa_type_propagation.cc \
trampolines/trampoline_compiler.cc \
utils/arena_allocator.cc \
utils/arena_bit_vector.cc \
@@ -118,6 +118,7 @@ LIBART_COMPILER_SRC_FILES := \
compilers.cc \
compiler.cc \
elf_fixup.cc \
+ elf_patcher.cc \
elf_stripper.cc \
elf_writer.cc \
elf_writer_quick.cc \
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index d1d5ad9715..7395324ea4 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -172,7 +172,7 @@ class NullCheckEliminationAndTypeInference : public PassME {
class ClassInitCheckElimination : public PassME {
public:
ClassInitCheckElimination()
- : PassME("ClInitCheckElimination", kRepeatingTopologicalSortTraversal) {
+ : PassME("ClInitCheckElimination", kLoopRepeatingTopologicalSortTraversal) {
}
bool Gate(const PassDataHolder* data) const {
@@ -207,17 +207,17 @@ class ClassInitCheckElimination : public PassME {
class GlobalValueNumberingPass : public PassME {
public:
GlobalValueNumberingPass()
- : PassME("GVN", kRepeatingTopologicalSortTraversal, "4_post_gvn_cfg") {
+ : PassME("GVN", kLoopRepeatingTopologicalSortTraversal, "4_post_gvn_cfg") {
}
- bool Gate(const PassDataHolder* data) const {
+ bool Gate(const PassDataHolder* data) const OVERRIDE {
DCHECK(data != nullptr);
CompilationUnit* cUnit = down_cast<const PassMEDataHolder*>(data)->c_unit;
DCHECK(cUnit != nullptr);
return cUnit->mir_graph->ApplyGlobalValueNumberingGate();
}
- bool Worker(const PassDataHolder* data) const {
+ bool Worker(const PassDataHolder* data) const OVERRIDE {
DCHECK(data != nullptr);
const PassMEDataHolder* pass_me_data_holder = down_cast<const PassMEDataHolder*>(data);
CompilationUnit* cUnit = pass_me_data_holder->c_unit;
@@ -227,7 +227,7 @@ class GlobalValueNumberingPass : public PassME {
return cUnit->mir_graph->ApplyGlobalValueNumbering(bb);
}
- void End(PassDataHolder* data) const {
+ void End(PassDataHolder* data) const OVERRIDE {
DCHECK(data != nullptr);
CompilationUnit* cUnit = down_cast<PassMEDataHolder*>(data)->c_unit;
DCHECK(cUnit != nullptr);
diff --git a/compiler/dex/dataflow_iterator-inl.h b/compiler/dex/dataflow_iterator-inl.h
index f8b9c1a952..d1abf7fa12 100644
--- a/compiler/dex/dataflow_iterator-inl.h
+++ b/compiler/dex/dataflow_iterator-inl.h
@@ -121,6 +121,56 @@ inline BasicBlock* AllNodesIterator::Next(bool had_change) {
return res;
}
+inline BasicBlock* LoopRepeatingTopologicalSortIterator::Next(bool had_change) {
+ if (idx_ != 0) {
+ // Mark last processed block visited.
+ BasicBlock* bb = mir_graph_->GetBasicBlock(block_id_list_->Get(idx_ - 1));
+ bb->visited = true;
+ if (had_change) {
+ // If we had a change we need to revisit the children.
+ ChildBlockIterator iter(bb, mir_graph_);
+ for (BasicBlock* child_bb = iter.Next(); child_bb != nullptr; child_bb = iter.Next()) {
+ child_bb->visited = false;
+ }
+ }
+ }
+
+ while (true) {
+ // Pop loops we have left and check if we need to recalculate one of them.
+ // NOTE: We need to do this even if idx_ == end_idx_.
+ while (loop_head_stack_->Size() != 0u &&
+ loop_ends_->Get(loop_head_stack_->Peek().first) == idx_) {
+ auto top = loop_head_stack_->Peek();
+ uint16_t loop_head_idx = top.first;
+ bool recalculated = top.second;
+ loop_head_stack_->Pop();
+ BasicBlock* loop_head = mir_graph_->GetBasicBlock(block_id_list_->Get(loop_head_idx));
+ DCHECK(loop_head != nullptr);
+ if (!recalculated || !loop_head->visited) {
+ loop_head_stack_->Insert(std::make_pair(loop_head_idx, true)); // Recalculating this loop.
+ idx_ = loop_head_idx + 1;
+ return loop_head;
+ }
+ }
+
+ if (idx_ == end_idx_) {
+ return nullptr;
+ }
+
+ // Get next block and return it if unvisited.
+ BasicBlockId idx = idx_;
+ idx_ += 1;
+ BasicBlock* bb = mir_graph_->GetBasicBlock(block_id_list_->Get(idx));
+ DCHECK(bb != nullptr);
+ if (!bb->visited) {
+ if (loop_ends_->Get(idx) != 0u) {
+ loop_head_stack_->Insert(std::make_pair(idx, false)); // Not recalculating.
+ }
+ return bb;
+ }
+ }
+}
+
} // namespace art
#endif // ART_COMPILER_DEX_DATAFLOW_ITERATOR_INL_H_
diff --git a/compiler/dex/dataflow_iterator.h b/compiler/dex/dataflow_iterator.h
index 66c524fc98..06d6832340 100644
--- a/compiler/dex/dataflow_iterator.h
+++ b/compiler/dex/dataflow_iterator.h
@@ -388,6 +388,52 @@ namespace art {
}
};
+ /**
+ * @class LoopRepeatingTopologicalSortIterator
+ * @brief Used to perform a Topological Sort Iteration of a MIRGraph, repeating loops as needed.
+ * @details The iterator uses the visited flags to keep track of the blocks that need
+ * recalculation and keeps a stack of loop heads in the MIRGraph. At the end of the loop
+ * it returns back to the loop head if it needs to be recalculated. Due to the use of
+ * the visited flags and the loop head stack in the MIRGraph, it's not possible to use
+ * two iterators at the same time or modify this data during iteration (though inspection
+ * of this data is allowed and sometimes even expected).
+ *
+ * NOTE: This iterator is not suitable for passes that need to propagate changes to
+ * predecessors, such as type inferrence.
+ */
+ class LoopRepeatingTopologicalSortIterator : public DataflowIterator {
+ public:
+ /**
+ * @brief The constructor, using all of the reachable blocks of the MIRGraph.
+ * @param mir_graph The MIRGraph considered.
+ */
+ explicit LoopRepeatingTopologicalSortIterator(MIRGraph* mir_graph)
+ : DataflowIterator(mir_graph, 0, mir_graph->GetTopologicalSortOrder()->Size()),
+ loop_ends_(mir_graph->GetTopologicalSortOrderLoopEnds()),
+ loop_head_stack_(mir_graph_->GetTopologicalSortOrderLoopHeadStack()) {
+ // Extra setup for RepeatingTopologicalSortIterator.
+ idx_ = start_idx_;
+ block_id_list_ = mir_graph->GetTopologicalSortOrder();
+ // Clear visited flags and check that the loop head stack is empty.
+ mir_graph->ClearAllVisitedFlags();
+ DCHECK_EQ(loop_head_stack_->Size(), 0u);
+ }
+
+ ~LoopRepeatingTopologicalSortIterator() {
+ DCHECK_EQ(loop_head_stack_->Size(), 0u);
+ }
+
+ /**
+ * @brief Get the next BasicBlock depending on iteration order.
+ * @param had_change did the user of the iteration change the previous BasicBlock.
+ * @return the next BasicBlock following the iteration order, 0 if finished.
+ */
+ virtual BasicBlock* Next(bool had_change = false) OVERRIDE;
+
+ private:
+ const GrowableArray<BasicBlockId>* const loop_ends_;
+ GrowableArray<std::pair<uint16_t, bool>>* const loop_head_stack_;
+ };
} // namespace art
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index d097500a77..51446f6356 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <cstdint>
+
#include "compiler.h"
#include "compiler_internals.h"
#include "driver/compiler_driver.h"
@@ -470,6 +472,10 @@ static const size_t kUnsupportedOpcodesSize[] = {
COMPILE_ASSERT(sizeof(kUnsupportedOpcodesSize) == 8 * sizeof(size_t),
kUnsupportedOpcodesSize_unexp);
+// The maximum amount of Dalvik register in a method for which we will start compiling. Tries to
+// avoid an abort when we need to manage more SSA registers than we can.
+static constexpr size_t kMaxAllowedDalvikRegisters = INT16_MAX / 2;
+
CompilationUnit::CompilationUnit(ArenaPool* pool)
: compiler_driver(nullptr),
class_linker(nullptr),
@@ -548,6 +554,12 @@ static bool CanCompileShorty(const char* shorty, InstructionSet instruction_set)
// Skip the method that we do not support currently.
static bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file,
CompilationUnit& cu) {
+ // This is a limitation in mir_graph. See MirGraph::SetNumSSARegs.
+ if (cu.num_dalvik_registers > kMaxAllowedDalvikRegisters) {
+ VLOG(compiler) << "Too many dalvik registers : " << cu.num_dalvik_registers;
+ return false;
+ }
+
// Check whether we do have limitations at all.
if (kSupportedTypes[cu.instruction_set] == nullptr &&
kUnsupportedOpcodesSize[cu.instruction_set] == 0U) {
diff --git a/compiler/dex/global_value_numbering.cc b/compiler/dex/global_value_numbering.cc
index 614e826fa6..d86be4e0e7 100644
--- a/compiler/dex/global_value_numbering.cc
+++ b/compiler/dex/global_value_numbering.cc
@@ -22,8 +22,10 @@ namespace art {
GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator)
: cu_(cu),
+ mir_graph_(cu->mir_graph.get()),
allocator_(allocator),
- repeat_count_(0u),
+ bbs_processed_(0u),
+ max_bbs_to_process_(kMaxBbsToProcessMultiplyFactor * mir_graph_->GetNumReachableBlocks()),
last_value_(0u),
modifications_allowed_(false),
global_value_map_(std::less<uint64_t>(), allocator->Adapter()),
@@ -32,10 +34,9 @@ GlobalValueNumbering::GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAlloc
array_location_map_(ArrayLocationComparator(), allocator->Adapter()),
array_location_reverse_map_(allocator->Adapter()),
ref_set_map_(std::less<ValueNameSet>(), allocator->Adapter()),
- lvns_(cu_->mir_graph->GetNumBlocks(), nullptr, allocator->Adapter()),
+ lvns_(mir_graph_->GetNumBlocks(), nullptr, allocator->Adapter()),
work_lvn_(nullptr),
merge_lvns_(allocator->Adapter()) {
- cu_->mir_graph->ClearAllVisitedFlags();
}
GlobalValueNumbering::~GlobalValueNumbering() {
@@ -46,21 +47,15 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb) {
if (UNLIKELY(!Good())) {
return nullptr;
}
- if (bb->data_flow_info == nullptr) {
+ if (UNLIKELY(bb->data_flow_info == nullptr)) {
return nullptr;
}
- if (bb->block_type == kEntryBlock) {
- repeat_count_ += 1u;
- if (repeat_count_ > kMaxRepeatCount) {
- last_value_ = kNoValue; // Make bad.
- return nullptr;
- }
- }
- if (bb->block_type == kExitBlock) {
+ if (UNLIKELY(bb->block_type == kExitBlock)) {
DCHECK(bb->first_mir_insn == nullptr);
return nullptr;
}
- if (bb->visited) {
+ if (UNLIKELY(bbs_processed_ == max_bbs_to_process_)) {
+ last_value_ = kNoValue; // Make bad.
return nullptr;
}
DCHECK(work_lvn_.get() == nullptr);
@@ -72,13 +67,34 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb) {
work_lvn_->SetSRegNullChecked(this_reg);
}
} else {
- // Merge all incoming arcs.
// To avoid repeated allocation on the ArenaStack, reuse a single vector kept as a member.
DCHECK(merge_lvns_.empty());
+ // If we're running the full GVN, the RepeatingTopologicalSortIterator keeps the loop
+ // head stack in the MIRGraph up to date and for a loop head we need to check whether
+ // we're making the initial computation and need to merge only preceding blocks in the
+ // topological order, or we're recalculating a loop head and need to merge all incoming
+ // LVNs. When we're not at a loop head (including having an empty loop head stack) all
+ // predecessors should be preceding blocks and we shall merge all of them anyway.
+ //
+ // If we're running the modification phase of the full GVN, the loop head stack will be
+ // empty and we need to merge all incoming LVNs. If we're running just a simple LVN,
+ // the loop head stack will also be empty and there will be nothing to merge anyway.
+ bool use_all_predecessors = true;
+ uint16_t loop_head_idx = 0u; // Used only if !use_all_predecessors.
+ if (mir_graph_->GetTopologicalSortOrderLoopHeadStack()->Size() != 0) {
+ // Full GVN inside a loop, see if we're at the loop head for the first time.
+ auto top = mir_graph_->GetTopologicalSortOrderLoopHeadStack()->Peek();
+ loop_head_idx = top.first;
+ bool recalculating = top.second;
+ use_all_predecessors = recalculating ||
+ loop_head_idx != mir_graph_->GetTopologicalSortOrderIndexes()->Get(bb->id);
+ }
GrowableArray<BasicBlockId>::Iterator iter(bb->predecessors);
- for (BasicBlock* pred_bb = cu_->mir_graph->GetBasicBlock(iter.Next());
- pred_bb != nullptr; pred_bb = cu_->mir_graph->GetBasicBlock(iter.Next())) {
- if (lvns_[pred_bb->id] != nullptr) {
+ for (BasicBlock* pred_bb = mir_graph_->GetBasicBlock(iter.Next());
+ pred_bb != nullptr; pred_bb = mir_graph_->GetBasicBlock(iter.Next())) {
+ if (lvns_[pred_bb->id] != nullptr &&
+ (use_all_predecessors ||
+ mir_graph_->GetTopologicalSortOrderIndexes()->Get(pred_bb->id) < loop_head_idx)) {
merge_lvns_.push_back(lvns_[pred_bb->id]);
}
}
@@ -87,19 +103,22 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb) {
if (bb->catch_entry) {
merge_type = LocalValueNumbering::kCatchMerge;
} else if (bb->last_mir_insn != nullptr &&
- (bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN ||
+ (bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN_VOID ||
+ bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN ||
bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN_OBJECT ||
bb->last_mir_insn->dalvikInsn.opcode == Instruction::RETURN_WIDE) &&
(bb->first_mir_insn == bb->last_mir_insn ||
- (bb->first_mir_insn->next == bb->last_mir_insn &&
- static_cast<int>(bb->first_mir_insn->dalvikInsn.opcode) == kMirOpPhi))) {
+ (static_cast<int>(bb->first_mir_insn->dalvikInsn.opcode) == kMirOpPhi &&
+ (bb->first_mir_insn->next == bb->last_mir_insn ||
+ (static_cast<int>(bb->first_mir_insn->next->dalvikInsn.opcode) == kMirOpPhi &&
+ bb->first_mir_insn->next->next == bb->last_mir_insn))))) {
merge_type = LocalValueNumbering::kReturnMerge;
}
// At least one predecessor must have been processed before this bb.
CHECK(!merge_lvns_.empty());
if (merge_lvns_.size() == 1u) {
work_lvn_->MergeOne(*merge_lvns_[0], merge_type);
- BasicBlock* pred_bb = cu_->mir_graph->GetBasicBlock(merge_lvns_[0]->Id());
+ BasicBlock* pred_bb = mir_graph_->GetBasicBlock(merge_lvns_[0]->Id());
if (HasNullCheckLastInsn(pred_bb, bb->id)) {
work_lvn_->SetSRegNullChecked(pred_bb->last_mir_insn->ssa_rep->uses[0]);
}
@@ -112,32 +131,13 @@ LocalValueNumbering* GlobalValueNumbering::PrepareBasicBlock(BasicBlock* bb) {
bool GlobalValueNumbering::FinishBasicBlock(BasicBlock* bb) {
DCHECK(work_lvn_ != nullptr);
- DCHECK(bb->id == work_lvn_->Id());
+ DCHECK_EQ(bb->id, work_lvn_->Id());
+ ++bbs_processed_;
merge_lvns_.clear();
- bool change = false;
- // Look for a branch to self or an already processed child.
- // (No need to repeat the LVN if all children are processed later.)
- ChildBlockIterator iter(bb, cu_->mir_graph.get());
- for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) {
- if (child == bb || lvns_[child->id] != nullptr) {
- // If we found an already processed child, check if the LVN actually differs.
- change = (lvns_[bb->id] == nullptr || !lvns_[bb->id]->Equals(*work_lvn_));
- break;
- }
- }
-
std::unique_ptr<const LocalValueNumbering> old_lvn(lvns_[bb->id]);
lvns_[bb->id] = work_lvn_.release();
-
- bb->visited = true;
- if (change) {
- ChildBlockIterator iter(bb, cu_->mir_graph.get());
- for (BasicBlock* child = iter.Next(); child != nullptr; child = iter.Next()) {
- child->visited = false;
- }
- }
- return change;
+ return (old_lvn == nullptr) || !old_lvn->Equals(*lvns_[bb->id]);
}
uint16_t GlobalValueNumbering::GetFieldId(const MirFieldInfo& field_info, uint16_t type) {
@@ -188,7 +188,7 @@ bool GlobalValueNumbering::NullCheckedInAllPredecessors(
uint16_t value_name = merge_names[i];
if (!pred_lvn->IsValueNullChecked(value_name)) {
// Check if the predecessor has an IF_EQZ/IF_NEZ as the last insn.
- const BasicBlock* pred_bb = cu_->mir_graph->GetBasicBlock(pred_lvn->Id());
+ const BasicBlock* pred_bb = mir_graph_->GetBasicBlock(pred_lvn->Id());
if (!HasNullCheckLastInsn(pred_bb, work_lvn_->Id())) {
return false;
}
diff --git a/compiler/dex/global_value_numbering.h b/compiler/dex/global_value_numbering.h
index 7ab77b733f..a12a7791ab 100644
--- a/compiler/dex/global_value_numbering.h
+++ b/compiler/dex/global_value_numbering.h
@@ -31,7 +31,10 @@ class GlobalValueNumbering {
GlobalValueNumbering(CompilationUnit* cu, ScopedArenaAllocator* allocator);
~GlobalValueNumbering();
+ // Prepare LVN for the basic block.
LocalValueNumbering* PrepareBasicBlock(BasicBlock* bb);
+
+ // Finish processing the basic block.
bool FinishBasicBlock(BasicBlock* bb);
// Checks that the value names didn't overflow.
@@ -42,7 +45,6 @@ class GlobalValueNumbering {
// Allow modifications.
void AllowModifications() {
DCHECK(Good());
- cu_->mir_graph->ClearAllVisitedFlags();
modifications_allowed_ = true;
}
@@ -182,7 +184,7 @@ class GlobalValueNumbering {
}
const BasicBlock* GetBasicBlock(uint16_t bb_id) const {
- return cu_->mir_graph->GetBasicBlock(bb_id);
+ return mir_graph_->GetBasicBlock(bb_id);
}
static bool HasNullCheckLastInsn(const BasicBlock* pred_bb, BasicBlockId succ_id);
@@ -194,7 +196,7 @@ class GlobalValueNumbering {
}
MIRGraph* GetMirGraph() const {
- return cu_->mir_graph.get();
+ return mir_graph_;
}
ScopedArenaAllocator* Allocator() const {
@@ -202,12 +204,16 @@ class GlobalValueNumbering {
}
CompilationUnit* const cu_;
+ MIRGraph* mir_graph_;
ScopedArenaAllocator* const allocator_;
- static constexpr uint32_t kMaxRepeatCount = 10u;
+ // The number of BBs that we need to process grows exponentially with the number
+ // of nested loops. Don't allow excessive processing for too many nested loops or
+ // otherwise expensive methods.
+ static constexpr uint32_t kMaxBbsToProcessMultiplyFactor = 20u;
- // Track the repeat count to make sure the GVN converges quickly and abort the GVN otherwise.
- uint32_t repeat_count_;
+ uint32_t bbs_processed_;
+ uint32_t max_bbs_to_process_;
// We have 32-bit last_value_ so that we can detect when we run out of value names, see Good().
// We usually don't check Good() until the end of LVN unless we're about to modify code.
diff --git a/compiler/dex/global_value_numbering_test.cc b/compiler/dex/global_value_numbering_test.cc
index 40bd9834ba..c82d2310c9 100644
--- a/compiler/dex/global_value_numbering_test.cc
+++ b/compiler/dex/global_value_numbering_test.cc
@@ -273,23 +273,20 @@ class GlobalValueNumberingTest : public testing::Test {
}
void PerformGVN() {
- cu_.mir_graph->SSATransformationStart();
- cu_.mir_graph->ComputeDFSOrders();
- cu_.mir_graph->ComputeDominators();
- cu_.mir_graph->ComputeTopologicalSortOrder();
- cu_.mir_graph->SSATransformationEnd();
- DoPerformGVN<RepeatingPreOrderDfsIterator>();
+ DoPerformGVN<LoopRepeatingTopologicalSortIterator>();
}
void PerformPreOrderDfsGVN() {
- cu_.mir_graph->SSATransformationStart();
- cu_.mir_graph->ComputeDFSOrders();
- cu_.mir_graph->SSATransformationEnd();
DoPerformGVN<RepeatingPreOrderDfsIterator>();
}
template <typename IteratorType>
void DoPerformGVN() {
+ cu_.mir_graph->SSATransformationStart();
+ cu_.mir_graph->ComputeDFSOrders();
+ cu_.mir_graph->ComputeDominators();
+ cu_.mir_graph->ComputeTopologicalSortOrder();
+ cu_.mir_graph->SSATransformationEnd();
ASSERT_TRUE(gvn_ == nullptr);
gvn_.reset(new (allocator_.get()) GlobalValueNumbering(&cu_, allocator_.get()));
ASSERT_FALSE(gvn_->CanModify());
@@ -313,7 +310,7 @@ class GlobalValueNumberingTest : public testing::Test {
ASSERT_TRUE(gvn_->Good());
ASSERT_FALSE(gvn_->CanModify());
gvn_->AllowModifications();
- PreOrderDfsIterator iterator(cu_.mir_graph.get());
+ TopologicalSortIterator iterator(cu_.mir_graph.get());
for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) {
LocalValueNumbering* lvn = gvn_->PrepareBasicBlock(bb);
if (lvn != nullptr) {
@@ -340,7 +337,6 @@ class GlobalValueNumberingTest : public testing::Test {
cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test.
allocator_.reset(ScopedArenaAllocator::Create(&cu_.arena_stack));
- // gvn_->AllowModifications();
}
ArenaPool pool_;
@@ -1917,7 +1913,7 @@ TEST_F(GlobalValueNumberingTest, InfiniteLocationLoop) {
PerformPreOrderDfsGVN();
}
-TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, DISABLED_IFieldAndPhi) {
+TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, IFieldAndPhi) {
static const IFieldDef ifields[] = {
{ 0u, 1u, 0u, false }, // Int.
};
@@ -1954,7 +1950,7 @@ TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, DISABLED_IFieldAndPhi) {
EXPECT_EQ(value_names_[5], value_names_[12]);
}
-TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, DISABLED_NullCheck) {
+TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, NullCheck) {
static const IFieldDef ifields[] = {
{ 0u, 1u, 0u, false }, // Int.
};
@@ -2024,14 +2020,10 @@ TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, DISABLED_NullCheck) {
EXPECT_NE(value_names_[2], value_names_[6]);
EXPECT_NE(value_names_[3], value_names_[7]);
EXPECT_NE(value_names_[4], value_names_[8]);
- EXPECT_NE(value_names_[0], value_names_[12]);
- EXPECT_NE(value_names_[1], value_names_[13]);
- EXPECT_NE(value_names_[2], value_names_[14]);
- EXPECT_NE(value_names_[3], value_names_[15]);
EXPECT_EQ(value_names_[4], value_names_[12]);
- EXPECT_NE(value_names_[5], value_names_[13]);
- EXPECT_NE(value_names_[6], value_names_[14]);
- EXPECT_NE(value_names_[7], value_names_[15]);
+ EXPECT_EQ(value_names_[5], value_names_[13]);
+ EXPECT_EQ(value_names_[6], value_names_[14]);
+ EXPECT_EQ(value_names_[7], value_names_[15]);
EXPECT_EQ(value_names_[12], value_names_[20]);
EXPECT_EQ(value_names_[13], value_names_[21]);
EXPECT_EQ(value_names_[14], value_names_[22]);
@@ -2049,7 +2041,7 @@ TEST_F(GlobalValueNumberingTestTwoConsecutiveLoops, DISABLED_NullCheck) {
}
}
-TEST_F(GlobalValueNumberingTestTwoNestedLoops, DISABLED_IFieldAndPhi) {
+TEST_F(GlobalValueNumberingTestTwoNestedLoops, IFieldAndPhi) {
static const IFieldDef ifields[] = {
{ 0u, 1u, 0u, false }, // Int.
};
@@ -2090,4 +2082,37 @@ TEST_F(GlobalValueNumberingTestTwoNestedLoops, DISABLED_IFieldAndPhi) {
EXPECT_EQ(value_names_[3], value_names_[14]);
}
+TEST_F(GlobalValueNumberingTest, NormalPathToCatchEntry) {
+ // When there's an empty catch block, all the exception paths lead to the next block in
+ // the normal path and we can also have normal "taken" or "fall-through" branches to that
+ // path. Check that LocalValueNumbering::PruneNonAliasingRefsForCatch() can handle it.
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)),
+ };
+ static const MIRDef mirs[] = {
+ DEF_INVOKE1(4, Instruction::INVOKE_STATIC, 100u),
+ };
+ PrepareBasicBlocks(bbs);
+ BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
+ catch_handler->catch_entry = true;
+ // Add successor block info to the check block.
+ BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
+ check_bb->successor_block_list_type = kCatch;
+ check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
+ &cu_.arena, 2, kGrowableArraySuccessorBlocks);
+ SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
+ (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+ successor_block_info->block = catch_handler->id;
+ check_bb->successor_blocks->Insert(successor_block_info);
+ BasicBlock* merge_block = cu_.mir_graph->GetBasicBlock(4u);
+ std::swap(merge_block->taken, merge_block->fall_through);
+ PrepareMIRs(mirs);
+ PerformGVN();
+}
+
} // namespace art
diff --git a/compiler/dex/local_value_numbering.cc b/compiler/dex/local_value_numbering.cc
index d5fd6feecf..0e072ecb6c 100644
--- a/compiler/dex/local_value_numbering.cc
+++ b/compiler/dex/local_value_numbering.cc
@@ -445,6 +445,11 @@ void LocalValueNumbering::MergeMemoryVersions(bool clobbered_catch) {
void LocalValueNumbering::PruneNonAliasingRefsForCatch() {
for (const LocalValueNumbering* lvn : gvn_->merge_lvns_) {
const BasicBlock* bb = gvn_->GetBasicBlock(lvn->Id());
+ if (UNLIKELY(bb->taken == id_) || UNLIKELY(bb->fall_through == id_)) {
+ // Non-exceptional path to a catch handler means that the catch block was actually
+ // empty and all exceptional paths lead to the shared path after that empty block.
+ continue;
+ }
DCHECK_EQ(bb->taken, kNullBlock);
DCHECK_NE(bb->fall_through, kNullBlock);
const BasicBlock* fall_through_bb = gvn_->GetBasicBlock(bb->fall_through);
@@ -529,6 +534,10 @@ void LocalValueNumbering::InPlaceIntersectMaps(Map* work_map, const Map& other_m
(!cmp(entry, *work_it) && !(work_it->second == entry.second)))) {
work_it = work_map->erase(work_it);
}
+ if (work_it == work_end) {
+ return;
+ }
+ ++work_it;
}
}
@@ -850,13 +859,18 @@ void LocalValueNumbering::Merge(MergeType merge_type) {
MergeMemoryVersions(merge_type == kCatchMerge);
// Merge non-aliasing maps/sets.
- MergeSets<IFieldLocToValueMap, &LocalValueNumbering::non_aliasing_ifield_value_map_,
- &LocalValueNumbering::MergeNonAliasingIFieldValues>();
- MergeSets<NonAliasingArrayValuesMap, &LocalValueNumbering::non_aliasing_array_value_map_,
- &LocalValueNumbering::MergeAliasingValues<
- NonAliasingArrayValuesMap, &LocalValueNumbering::non_aliasing_array_value_map_,
- NonAliasingArrayVersions>>();
IntersectSets<ValueNameSet, &LocalValueNumbering::non_aliasing_refs_>();
+ if (!non_aliasing_refs_.empty() && merge_type == kCatchMerge) {
+ PruneNonAliasingRefsForCatch();
+ }
+ if (!non_aliasing_refs_.empty()) {
+ MergeSets<IFieldLocToValueMap, &LocalValueNumbering::non_aliasing_ifield_value_map_,
+ &LocalValueNumbering::MergeNonAliasingIFieldValues>();
+ MergeSets<NonAliasingArrayValuesMap, &LocalValueNumbering::non_aliasing_array_value_map_,
+ &LocalValueNumbering::MergeAliasingValues<
+ NonAliasingArrayValuesMap, &LocalValueNumbering::non_aliasing_array_value_map_,
+ NonAliasingArrayVersions>>();
+ }
// We won't do anything complicated for range checks, just calculate the intersection.
IntersectSets<RangeCheckSet, &LocalValueNumbering::range_checked_>();
@@ -867,7 +881,6 @@ void LocalValueNumbering::Merge(MergeType merge_type) {
if (merge_type == kCatchMerge) {
// Memory is clobbered. New memory version already created, don't merge aliasing locations.
- PruneNonAliasingRefsForCatch();
return;
}
@@ -1356,8 +1369,8 @@ uint16_t LocalValueNumbering::GetValueNumber(MIR* mir) {
case Instruction::MONITOR_EXIT:
HandleNullCheck(mir, GetOperandValue(mir->ssa_rep->uses[0]));
// If we're running GVN and CanModify(), uneliminated null check indicates bytecode error.
- if ((gvn_->cu_->disable_opt & (1 << kGlobalValueNumbering)) == 0 && gvn_->CanModify() &&
- (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
+ if ((gvn_->GetCompilationUnit()->disable_opt & (1u << kGlobalValueNumbering)) == 0u &&
+ gvn_->CanModify() && (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) == 0) {
LOG(WARNING) << "Bytecode error: MONITOR_EXIT is still null checked at 0x" << std::hex
<< mir->offset << " in " << PrettyMethod(gvn_->cu_->method_idx, *gvn_->cu_->dex_file);
}
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 1c8a9b5079..331af21eda 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -84,6 +84,9 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
dfs_post_order_(NULL),
dom_post_order_traversal_(NULL),
topological_order_(nullptr),
+ topological_order_loop_ends_(nullptr),
+ topological_order_indexes_(nullptr),
+ topological_order_loop_head_stack_(nullptr),
i_dom_list_(NULL),
def_block_matrix_(NULL),
temp_scoped_alloc_(),
@@ -1526,117 +1529,248 @@ void MIRGraph::SSATransformationEnd() {
temp_scoped_alloc_.reset();
}
-void MIRGraph::ComputeTopologicalSortOrder() {
- // Clear the nodes.
- ClearAllVisitedFlags();
+static BasicBlock* SelectTopologicalSortOrderFallBack(
+ MIRGraph* mir_graph, const ArenaBitVector* current_loop,
+ const ScopedArenaVector<size_t>* visited_cnt_values, ScopedArenaAllocator* allocator,
+ ScopedArenaVector<BasicBlockId>* tmp_stack) {
+ // No true loop head has been found but there may be true loop heads after the mess we need
+ // to resolve. To avoid taking one of those, pick the candidate with the highest number of
+ // reachable unvisited nodes. That candidate will surely be a part of a loop.
+ BasicBlock* fall_back = nullptr;
+ size_t fall_back_num_reachable = 0u;
+ // Reuse the same bit vector for each candidate to mark reachable unvisited blocks.
+ ArenaBitVector candidate_reachable(allocator, mir_graph->GetNumBlocks(), false, kBitMapMisc);
+ AllNodesIterator iter(mir_graph);
+ for (BasicBlock* candidate = iter.Next(); candidate != nullptr; candidate = iter.Next()) {
+ if (candidate->hidden || // Hidden, or
+ candidate->visited || // already processed, or
+ (*visited_cnt_values)[candidate->id] == 0u || // no processed predecessors, or
+ (current_loop != nullptr && // outside current loop.
+ !current_loop->IsBitSet(candidate->id))) {
+ continue;
+ }
+ DCHECK(tmp_stack->empty());
+ tmp_stack->push_back(candidate->id);
+ candidate_reachable.ClearAllBits();
+ size_t num_reachable = 0u;
+ while (!tmp_stack->empty()) {
+ BasicBlockId current_id = tmp_stack->back();
+ tmp_stack->pop_back();
+ BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id);
+ DCHECK(current_bb != nullptr);
+ ChildBlockIterator child_iter(current_bb, mir_graph);
+ BasicBlock* child_bb = child_iter.Next();
+ for ( ; child_bb != nullptr; child_bb = child_iter.Next()) {
+ DCHECK(!child_bb->hidden);
+ if (child_bb->visited || // Already processed, or
+ (current_loop != nullptr && // outside current loop.
+ !current_loop->IsBitSet(child_bb->id))) {
+ continue;
+ }
+ if (!candidate_reachable.IsBitSet(child_bb->id)) {
+ candidate_reachable.SetBit(child_bb->id);
+ tmp_stack->push_back(child_bb->id);
+ num_reachable += 1u;
+ }
+ }
+ }
+ if (fall_back_num_reachable < num_reachable) {
+ fall_back_num_reachable = num_reachable;
+ fall_back = candidate;
+ }
+ }
+ return fall_back;
+}
- // Create the topological order if need be.
- if (topological_order_ == nullptr) {
- topological_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, GetNumBlocks());
+// Compute from which unvisited blocks is bb_id reachable through unvisited blocks.
+static void ComputeUnvisitedReachableFrom(MIRGraph* mir_graph, BasicBlockId bb_id,
+ ArenaBitVector* reachable,
+ ScopedArenaVector<BasicBlockId>* tmp_stack) {
+ // NOTE: Loop heads indicated by the "visited" flag.
+ DCHECK(tmp_stack->empty());
+ reachable->ClearAllBits();
+ tmp_stack->push_back(bb_id);
+ while (!tmp_stack->empty()) {
+ BasicBlockId current_id = tmp_stack->back();
+ tmp_stack->pop_back();
+ BasicBlock* current_bb = mir_graph->GetBasicBlock(current_id);
+ DCHECK(current_bb != nullptr);
+ GrowableArray<BasicBlockId>::Iterator iter(current_bb->predecessors);
+ BasicBlock* pred_bb = mir_graph->GetBasicBlock(iter.Next());
+ for ( ; pred_bb != nullptr; pred_bb = mir_graph->GetBasicBlock(iter.Next())) {
+ if (!pred_bb->visited && !reachable->IsBitSet(pred_bb->id)) {
+ reachable->SetBit(pred_bb->id);
+ tmp_stack->push_back(pred_bb->id);
+ }
+ }
}
- topological_order_->Reset();
+}
+void MIRGraph::ComputeTopologicalSortOrder() {
ScopedArenaAllocator allocator(&cu_->arena_stack);
+ unsigned int num_blocks = GetNumBlocks();
+
ScopedArenaQueue<BasicBlock*> q(allocator.Adapter());
- ScopedArenaVector<size_t> visited_cnt_values(GetNumBlocks(), 0u, allocator.Adapter());
+ ScopedArenaVector<size_t> visited_cnt_values(num_blocks, 0u, allocator.Adapter());
+ ScopedArenaVector<BasicBlockId> loop_head_stack(allocator.Adapter());
+ size_t max_nested_loops = 0u;
+ ArenaBitVector loop_exit_blocks(&allocator, num_blocks, false, kBitMapMisc);
+ loop_exit_blocks.ClearAllBits();
- // Set up visitedCntValues map for all BB. The default value for this counters in the map is zero.
- // also fill initial queue.
+ // Count the number of blocks to process and add the entry block(s).
GrowableArray<BasicBlock*>::Iterator iterator(&block_list_);
-
- size_t num_blocks = 0u;
- while (true) {
- BasicBlock* bb = iterator.Next();
-
- if (bb == nullptr) {
- break;
- }
-
+ unsigned int num_blocks_to_process = 0u;
+ for (BasicBlock* bb = iterator.Next(); bb != nullptr; bb = iterator.Next()) {
if (bb->hidden == true) {
continue;
}
- num_blocks += 1u;
- size_t unvisited_predecessor_count = bb->predecessors->Size();
-
- GrowableArray<BasicBlockId>::Iterator pred_iterator(bb->predecessors);
- // To process loops we should not wait for dominators.
- while (true) {
- BasicBlock* pred_bb = GetBasicBlock(pred_iterator.Next());
-
- if (pred_bb == nullptr) {
- break;
- }
-
- // Skip the backward branch or hidden predecessor.
- if (pred_bb->hidden ||
- (pred_bb->dominators != nullptr && pred_bb->dominators->IsBitSet(bb->id))) {
- unvisited_predecessor_count -= 1u;
- }
- }
-
- visited_cnt_values[bb->id] = unvisited_predecessor_count;
+ num_blocks_to_process += 1u;
- // Add entry block to queue.
- if (unvisited_predecessor_count == 0) {
+ if (bb->predecessors->Size() == 0u) {
+ // Add entry block to the queue.
q.push(bb);
}
}
- // We can get a cycle where none of the blocks dominates the other. Therefore don't
- // stop when the queue is empty, continue until we've processed all the blocks.
- AllNodesIterator candidate_iter(this); // For the empty queue case.
- while (num_blocks != 0u) {
- num_blocks -= 1u;
+ // Create the topological order if need be.
+ if (topological_order_ == nullptr) {
+ topological_order_ = new (arena_) GrowableArray<BasicBlockId>(arena_, num_blocks);
+ topological_order_loop_ends_ = new (arena_) GrowableArray<uint16_t>(arena_, num_blocks);
+ topological_order_indexes_ = new (arena_) GrowableArray<uint16_t>(arena_, num_blocks);
+ }
+ topological_order_->Reset();
+ topological_order_loop_ends_->Reset();
+ topological_order_indexes_->Reset();
+ topological_order_loop_ends_->Resize(num_blocks);
+ topological_order_indexes_->Resize(num_blocks);
+ for (BasicBlockId i = 0; i != num_blocks; ++i) {
+ topological_order_loop_ends_->Insert(0u);
+ topological_order_indexes_->Insert(static_cast<uint16_t>(-1));
+ }
+
+ // Mark all blocks as unvisited.
+ ClearAllVisitedFlags();
+
+ // For loop heads, keep track from which blocks they are reachable not going through other
+ // loop heads. Other loop heads are excluded to detect the heads of nested loops. The children
+ // in this set go into the loop body, the other children are jumping over the loop.
+ ScopedArenaVector<ArenaBitVector*> loop_head_reachable_from(allocator.Adapter());
+ loop_head_reachable_from.resize(num_blocks, nullptr);
+ // Reuse the same temp stack whenever calculating a loop_head_reachable_from[loop_head_id].
+ ScopedArenaVector<BasicBlockId> tmp_stack(allocator.Adapter());
+
+ while (num_blocks_to_process != 0u) {
BasicBlock* bb = nullptr;
if (!q.empty()) {
+ num_blocks_to_process -= 1u;
// Get top.
bb = q.front();
q.pop();
+ if (bb->visited) {
+ // Loop head: it was already processed, mark end and copy exit blocks to the queue.
+ DCHECK(q.empty()) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ uint16_t idx = static_cast<uint16_t>(topological_order_->Size());
+ topological_order_loop_ends_->Put(topological_order_indexes_->Get(bb->id), idx);
+ DCHECK_EQ(loop_head_stack.back(), bb->id);
+ loop_head_stack.pop_back();
+ ArenaBitVector* reachable =
+ loop_head_stack.empty() ? nullptr : loop_head_reachable_from[loop_head_stack.back()];
+ for (BasicBlockId candidate_id : loop_exit_blocks.Indexes()) {
+ if (reachable == nullptr || reachable->IsBitSet(candidate_id)) {
+ q.push(GetBasicBlock(candidate_id));
+ // NOTE: The BitVectorSet::IndexIterator will not check the pointed-to bit again,
+ // so clearing the bit has no effect on the iterator.
+ loop_exit_blocks.ClearBit(candidate_id);
+ }
+ }
+ continue;
+ }
} else {
- // Find some block we didn't visit yet that has at least one visited predecessor.
- while (bb == nullptr) {
- BasicBlock* candidate = candidate_iter.Next();
- DCHECK(candidate != nullptr);
- if (candidate->visited || candidate->hidden) {
+ // Find the new loop head.
+ AllNodesIterator iter(this);
+ while (true) {
+ BasicBlock* candidate = iter.Next();
+ if (candidate == nullptr) {
+ // We did not find a true loop head, fall back to a reachable block in any loop.
+ ArenaBitVector* current_loop =
+ loop_head_stack.empty() ? nullptr : loop_head_reachable_from[loop_head_stack.back()];
+ bb = SelectTopologicalSortOrderFallBack(this, current_loop, &visited_cnt_values,
+ &allocator, &tmp_stack);
+ DCHECK(bb != nullptr) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
+ if (kIsDebugBuild && cu_->dex_file != nullptr) {
+ LOG(INFO) << "Topological sort order: Using fall-back in "
+ << PrettyMethod(cu_->method_idx, *cu_->dex_file) << " BB #" << bb->id
+ << " @0x" << std::hex << bb->start_offset
+ << ", num_blocks = " << std::dec << num_blocks;
+ }
+ break;
+ }
+ if (candidate->hidden || // Hidden, or
+ candidate->visited || // already processed, or
+ visited_cnt_values[candidate->id] == 0u || // no processed predecessors, or
+ (!loop_head_stack.empty() && // outside current loop.
+ !loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(candidate->id))) {
continue;
}
- GrowableArray<BasicBlockId>::Iterator iter(candidate->predecessors);
- for (BasicBlock* pred_bb = GetBasicBlock(iter.Next()); pred_bb != nullptr;
- pred_bb = GetBasicBlock(iter.Next())) {
- if (!pred_bb->hidden && pred_bb->visited) {
- bb = candidate;
- break;
+
+ GrowableArray<BasicBlockId>::Iterator pred_iter(candidate->predecessors);
+ BasicBlock* pred_bb = GetBasicBlock(pred_iter.Next());
+ for ( ; pred_bb != nullptr; pred_bb = GetBasicBlock(pred_iter.Next())) {
+ if (pred_bb != candidate && !pred_bb->visited &&
+ !pred_bb->dominators->IsBitSet(candidate->id)) {
+ break; // Keep non-null pred_bb to indicate failure.
}
}
+ if (pred_bb == nullptr) {
+ bb = candidate;
+ break;
+ }
}
+ // Compute blocks from which the loop head is reachable and process those blocks first.
+ ArenaBitVector* reachable =
+ new (&allocator) ArenaBitVector(&allocator, num_blocks, false, kBitMapMisc);
+ loop_head_reachable_from[bb->id] = reachable;
+ ComputeUnvisitedReachableFrom(this, bb->id, reachable, &tmp_stack);
+ // Now mark as loop head. (Even if it's only a fall back when we don't find a true loop.)
+ loop_head_stack.push_back(bb->id);
+ max_nested_loops = std::max(max_nested_loops, loop_head_stack.size());
}
DCHECK_EQ(bb->hidden, false);
DCHECK_EQ(bb->visited, false);
-
- // We've visited all the predecessors. So, we can visit bb.
bb->visited = true;
// Now add the basic block.
+ uint16_t idx = static_cast<uint16_t>(topological_order_->Size());
+ topological_order_indexes_->Put(bb->id, idx);
topological_order_->Insert(bb->id);
- // Reduce visitedCnt for all the successors and add into the queue ones with visitedCnt equals to zero.
+ // Update visited_cnt_values for children.
ChildBlockIterator succIter(bb, this);
BasicBlock* successor = succIter.Next();
for ( ; successor != nullptr; successor = succIter.Next()) {
- if (successor->visited || successor->hidden) {
+ if (successor->hidden) {
continue;
}
- // one more predecessor was visited.
- DCHECK_NE(visited_cnt_values[successor->id], 0u);
- visited_cnt_values[successor->id] -= 1u;
- if (visited_cnt_values[successor->id] == 0u) {
- q.push(successor);
+ // One more predecessor was visited.
+ visited_cnt_values[successor->id] += 1u;
+ if (visited_cnt_values[successor->id] == successor->predecessors->Size()) {
+ if (loop_head_stack.empty() ||
+ loop_head_reachable_from[loop_head_stack.back()]->IsBitSet(successor->id)) {
+ q.push(successor);
+ } else {
+ DCHECK(!loop_exit_blocks.IsBitSet(successor->id));
+ loop_exit_blocks.SetBit(successor->id);
+ }
}
}
}
+
+ // Prepare the loop head stack for iteration.
+ topological_order_loop_head_stack_ =
+ new (arena_) GrowableArray<std::pair<uint16_t, bool>>(arena_, max_nested_loops);
}
bool BasicBlock::IsExceptionBlock() const {
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 1556a19da7..768ae21c28 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -27,6 +27,7 @@
#include "mir_method_info.h"
#include "utils/arena_bit_vector.h"
#include "utils/growable_array.h"
+#include "utils/scoped_arena_containers.h"
#include "reg_location.h"
#include "reg_storage.h"
@@ -689,6 +690,21 @@ class MIRGraph {
return topological_order_;
}
+ GrowableArray<BasicBlockId>* GetTopologicalSortOrderLoopEnds() {
+ DCHECK(topological_order_loop_ends_ != nullptr);
+ return topological_order_loop_ends_;
+ }
+
+ GrowableArray<BasicBlockId>* GetTopologicalSortOrderIndexes() {
+ DCHECK(topological_order_indexes_ != nullptr);
+ return topological_order_indexes_;
+ }
+
+ GrowableArray<std::pair<uint16_t, bool>>* GetTopologicalSortOrderLoopHeadStack() {
+ DCHECK(topological_order_loop_head_stack_ != nullptr);
+ return topological_order_loop_head_stack_;
+ }
+
bool IsConst(int32_t s_reg) const {
return is_constant_v_->IsBitSet(s_reg);
}
@@ -727,7 +743,7 @@ class MIRGraph {
* would be filtered out with current settings. When orig_sreg field is removed
* from RegLocation, expand s_reg_low to handle all possible cases and remove DCHECK().
*/
- DCHECK_EQ(new_num, static_cast<int16_t>(new_num));
+ CHECK_EQ(new_num, static_cast<int16_t>(new_num));
num_ssa_regs_ = new_num;
}
@@ -1132,6 +1148,14 @@ class MIRGraph {
GrowableArray<BasicBlockId>* dfs_post_order_;
GrowableArray<BasicBlockId>* dom_post_order_traversal_;
GrowableArray<BasicBlockId>* topological_order_;
+ // Indexes in topological_order_ need to be only as big as the BasicBlockId.
+ COMPILE_ASSERT(sizeof(BasicBlockId) == sizeof(uint16_t), assuming_16_bit_BasicBlockId);
+ // For each loop head, remember the past-the-end index of the end of the loop. 0 if not loop head.
+ GrowableArray<uint16_t>* topological_order_loop_ends_;
+ // Map BB ids to topological_order_ indexes. 0xffff if not included (hidden or null block).
+ GrowableArray<uint16_t>* topological_order_indexes_;
+ // Stack of the loop head indexes and recalculation flags for RepeatingTopologicalSortIterator.
+ GrowableArray<std::pair<uint16_t, bool>>* topological_order_loop_head_stack_;
int* i_dom_list_;
ArenaBitVector** def_block_matrix_; // num_dalvik_register x num_blocks.
std::unique_ptr<ScopedArenaAllocator> temp_scoped_alloc_;
@@ -1177,6 +1201,7 @@ class MIRGraph {
friend class ClassInitCheckEliminationTest;
friend class GlobalValueNumberingTest;
friend class LocalValueNumberingTest;
+ friend class TopologicalSortOrderTest;
};
} // namespace art
diff --git a/compiler/dex/mir_graph_test.cc b/compiler/dex/mir_graph_test.cc
new file mode 100644
index 0000000000..932f453b3c
--- /dev/null
+++ b/compiler/dex/mir_graph_test.cc
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mir_graph.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+class TopologicalSortOrderTest : public testing::Test {
+ protected:
+ struct BBDef {
+ static constexpr size_t kMaxSuccessors = 4;
+ static constexpr size_t kMaxPredecessors = 4;
+
+ BBType type;
+ size_t num_successors;
+ BasicBlockId successors[kMaxPredecessors];
+ size_t num_predecessors;
+ BasicBlockId predecessors[kMaxPredecessors];
+ };
+
+#define DEF_SUCC0() \
+ 0u, { }
+#define DEF_SUCC1(s1) \
+ 1u, { s1 }
+#define DEF_SUCC2(s1, s2) \
+ 2u, { s1, s2 }
+#define DEF_SUCC3(s1, s2, s3) \
+ 3u, { s1, s2, s3 }
+#define DEF_SUCC4(s1, s2, s3, s4) \
+ 4u, { s1, s2, s3, s4 }
+#define DEF_PRED0() \
+ 0u, { }
+#define DEF_PRED1(p1) \
+ 1u, { p1 }
+#define DEF_PRED2(p1, p2) \
+ 2u, { p1, p2 }
+#define DEF_PRED3(p1, p2, p3) \
+ 3u, { p1, p2, p3 }
+#define DEF_PRED4(p1, p2, p3, p4) \
+ 4u, { p1, p2, p3, p4 }
+#define DEF_BB(type, succ, pred) \
+ { type, succ, pred }
+
+ void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
+ cu_.mir_graph->block_id_map_.clear();
+ cu_.mir_graph->block_list_.Reset();
+ ASSERT_LT(3u, count); // null, entry, exit and at least one bytecode block.
+ ASSERT_EQ(kNullBlock, defs[0].type);
+ ASSERT_EQ(kEntryBlock, defs[1].type);
+ ASSERT_EQ(kExitBlock, defs[2].type);
+ for (size_t i = 0u; i != count; ++i) {
+ const BBDef* def = &defs[i];
+ BasicBlock* bb = cu_.mir_graph->NewMemBB(def->type, i);
+ cu_.mir_graph->block_list_.Insert(bb);
+ if (def->num_successors <= 2) {
+ bb->successor_block_list_type = kNotUsed;
+ bb->successor_blocks = nullptr;
+ bb->fall_through = (def->num_successors >= 1) ? def->successors[0] : 0u;
+ bb->taken = (def->num_successors >= 2) ? def->successors[1] : 0u;
+ } else {
+ bb->successor_block_list_type = kPackedSwitch;
+ bb->fall_through = 0u;
+ bb->taken = 0u;
+ bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
+ &cu_.arena, def->num_successors, kGrowableArraySuccessorBlocks);
+ for (size_t j = 0u; j != def->num_successors; ++j) {
+ SuccessorBlockInfo* successor_block_info =
+ static_cast<SuccessorBlockInfo*>(cu_.arena.Alloc(sizeof(SuccessorBlockInfo),
+ kArenaAllocSuccessor));
+ successor_block_info->block = j;
+ successor_block_info->key = 0u; // Not used by class init check elimination.
+ bb->successor_blocks->Insert(successor_block_info);
+ }
+ }
+ bb->predecessors = new (&cu_.arena) GrowableArray<BasicBlockId>(
+ &cu_.arena, def->num_predecessors, kGrowableArrayPredecessors);
+ for (size_t j = 0u; j != def->num_predecessors; ++j) {
+ ASSERT_NE(0u, def->predecessors[j]);
+ bb->predecessors->Insert(def->predecessors[j]);
+ }
+ if (def->type == kDalvikByteCode || def->type == kEntryBlock || def->type == kExitBlock) {
+ bb->data_flow_info = static_cast<BasicBlockDataFlow*>(
+ cu_.arena.Alloc(sizeof(BasicBlockDataFlow), kArenaAllocDFInfo));
+ }
+ }
+ cu_.mir_graph->num_blocks_ = count;
+ ASSERT_EQ(count, cu_.mir_graph->block_list_.Size());
+ cu_.mir_graph->entry_block_ = cu_.mir_graph->block_list_.Get(1);
+ ASSERT_EQ(kEntryBlock, cu_.mir_graph->entry_block_->block_type);
+ cu_.mir_graph->exit_block_ = cu_.mir_graph->block_list_.Get(2);
+ ASSERT_EQ(kExitBlock, cu_.mir_graph->exit_block_->block_type);
+ }
+
+ template <size_t count>
+ void PrepareBasicBlocks(const BBDef (&defs)[count]) {
+ DoPrepareBasicBlocks(defs, count);
+ }
+
+ void ComputeTopologicalSortOrder() {
+ cu_.mir_graph->SSATransformationStart();
+ cu_.mir_graph->ComputeDFSOrders();
+ cu_.mir_graph->ComputeDominators();
+ cu_.mir_graph->ComputeTopologicalSortOrder();
+ cu_.mir_graph->SSATransformationEnd();
+ ASSERT_NE(cu_.mir_graph->topological_order_, nullptr);
+ ASSERT_NE(cu_.mir_graph->topological_order_loop_ends_, nullptr);
+ ASSERT_NE(cu_.mir_graph->topological_order_indexes_, nullptr);
+ ASSERT_EQ(cu_.mir_graph->GetNumBlocks(), cu_.mir_graph->topological_order_indexes_->Size());
+ for (size_t i = 0, size = cu_.mir_graph->GetTopologicalSortOrder()->Size(); i != size; ++i) {
+ ASSERT_LT(cu_.mir_graph->topological_order_->Get(i), cu_.mir_graph->GetNumBlocks());
+ BasicBlockId id = cu_.mir_graph->topological_order_->Get(i);
+ EXPECT_EQ(i, cu_.mir_graph->topological_order_indexes_->Get(id));
+ }
+ }
+
+ void DoCheckOrder(const BasicBlockId* ids, size_t count) {
+ ASSERT_EQ(count, cu_.mir_graph->GetTopologicalSortOrder()->Size());
+ for (size_t i = 0; i != count; ++i) {
+ EXPECT_EQ(ids[i], cu_.mir_graph->GetTopologicalSortOrder()->Get(i)) << i;
+ }
+ }
+
+ template <size_t count>
+ void CheckOrder(const BasicBlockId (&ids)[count]) {
+ DoCheckOrder(ids, count);
+ }
+
+ void DoCheckLoopEnds(const uint16_t* ends, size_t count) {
+ for (size_t i = 0; i != count; ++i) {
+ ASSERT_LT(i, cu_.mir_graph->GetTopologicalSortOrderLoopEnds()->Size());
+ EXPECT_EQ(ends[i], cu_.mir_graph->GetTopologicalSortOrderLoopEnds()->Get(i)) << i;
+ }
+ }
+
+ template <size_t count>
+ void CheckLoopEnds(const uint16_t (&ends)[count]) {
+ DoCheckLoopEnds(ends, count);
+ }
+
+ TopologicalSortOrderTest()
+ : pool_(),
+ cu_(&pool_) {
+ cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+ }
+
+ ArenaPool pool_;
+ CompilationUnit cu_;
+};
+
+TEST_F(TopologicalSortOrderTest, DoWhile) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 0, 3, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, While) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED2(1, 4)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(3)), // Loops to 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 3, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, WhileWithTwoBackEdges) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED3(1, 4, 5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED1(3)), // Loops to 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)), // Loops to 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 6, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 4, 0, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, NestedLoop) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED2(1, 6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED2(3, 5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to 4.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(3), DEF_PRED1(4)), // Loops to 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 6, 7, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 5, 4, 0, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, NestedLoopHeadLoops) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 4)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 3), DEF_PRED2(3, 5)), // Nested head, loops to 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to 4.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 6, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 4, 4, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, NestedLoopSameBackBranchBlock) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 6), DEF_PRED2(1, 5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 3), DEF_PRED1(4)), // Loops to 4 and 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 6, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 4, 4, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, TwoReorderedInnerLoops) {
+ // This is a simplified version of real code graph where the branch from 8 to 5 must prevent
+ // the block 5 from being considered a loop head before processing the loop 7-8.
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(9)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 9), DEF_PRED2(1, 5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 7), DEF_PRED1(3)), // Branch over loop in 5.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(6, 3), DEF_PRED3(4, 6, 8)), // Loops to 4; inner loop.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(5)), // Loops to 5.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(8), DEF_PRED2(4, 8)), // Loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 5), DEF_PRED1(7)), // Loops to 7; branches to 5.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(3)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 7, 8, 5, 6, 9, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 7, 0, 5, 0, 7, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, NestedLoopWithBackEdgeAfterOuterLoopBackEdge) {
+ // This is a simplified version of real code graph. The back-edge from 7 to the inner
+ // loop head 4 comes after the back-edge from 6 to the outer loop head 3. To make this
+ // appear a bit more complex, there's also a back-edge from 5 to 4.
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED2(1, 6)), // Outer loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 6), DEF_PRED3(3, 5, 7)), // Inner loop head.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(4)), // Loops to inner loop head 4.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(7, 3), DEF_PRED1(4)), // Loops to outer loop head 3.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(2, 4), DEF_PRED1(6)), // Loops to inner loop head 4.
+ };
+ const BasicBlockId expected_order[] = {
+ // NOTE: The 5 goes before 6 only because 5 is a "fall-through" from 4 while 6 is "taken".
+ 1, 3, 4, 5, 6, 7, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 6, 6, 0, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+TEST_F(TopologicalSortOrderTest, LoopWithTwoEntryPoints) {
+ const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(7)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED2(3, 6)), // Fall-back block is chosen as
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED2(3, 4)), // the earlier from these two.
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 7), DEF_PRED1(5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(6)),
+ };
+ const BasicBlockId expected_order[] = {
+ 1, 3, 4, 5, 6, 7, 2
+ };
+ const uint16_t loop_ends[] = {
+ 0, 0, 5, 0, 0, 0, 0
+ };
+
+ PrepareBasicBlocks(bbs);
+ ComputeTopologicalSortOrder();
+ CheckOrder(expected_order);
+ CheckLoopEnds(loop_ends);
+}
+
+} // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 869c48f66c..5c98654c32 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -321,7 +321,7 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
return true;
}
// Don't do a separate LVN if we did the GVN.
- bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1 << kGlobalValueNumbering)) != 0;
+ bool use_lvn = bb->use_lvn && (cu_->disable_opt & (1u << kGlobalValueNumbering)) != 0u;
std::unique_ptr<ScopedArenaAllocator> allocator;
std::unique_ptr<GlobalValueNumbering> global_valnum;
std::unique_ptr<LocalValueNumbering> local_valnum;
@@ -737,11 +737,9 @@ bool MIRGraph::EliminateNullChecksAndInferTypes(BasicBlock* bb) {
ArenaBitVector* ssa_regs_to_check = temp_bit_vector_;
if (do_nce) {
/*
- * Set initial state. Be conservative with catch
- * blocks and start with no assumptions about null check
- * status (except for "this").
+ * Set initial state. Catch blocks don't need any special treatment.
*/
- if ((bb->block_type == kEntryBlock) | bb->catch_entry) {
+ if (bb->block_type == kEntryBlock) {
ssa_regs_to_check->ClearAllBits();
// Assume all ins are objects.
for (uint16_t in_reg = cu_->num_dalvik_registers - cu_->num_ins;
@@ -1047,12 +1045,11 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
}
/*
- * Set initial state. Be conservative with catch
- * blocks and start with no assumptions about class init check status.
+ * Set initial state. Catch blocks don't need any special treatment.
*/
ArenaBitVector* classes_to_check = temp_bit_vector_;
DCHECK(classes_to_check != nullptr);
- if ((bb->block_type == kEntryBlock) | bb->catch_entry) {
+ if (bb->block_type == kEntryBlock) {
classes_to_check->SetInitialBits(temp_bit_vector_size_);
} else if (bb->predecessors->Size() == 1) {
BasicBlock* pred_bb = GetBasicBlock(bb->predecessors->Get(0));
@@ -1142,11 +1139,7 @@ void MIRGraph::EliminateClassInitChecksEnd() {
}
bool MIRGraph::ApplyGlobalValueNumberingGate() {
- if ((cu_->disable_opt & (1 << kGlobalValueNumbering)) != 0) {
- return false;
- }
-
- if ((merged_df_flags_ & DF_LVN) == 0) {
+ if ((cu_->disable_opt & (1u << kGlobalValueNumbering)) != 0u) {
return false;
}
@@ -1182,7 +1175,7 @@ void MIRGraph::ApplyGlobalValueNumberingEnd() {
lvn->GetValueNumber(mir);
}
bool change = temp_gvn_->FinishBasicBlock(bb);
- DCHECK(!change);
+ DCHECK(!change) << PrettyMethod(cu_->method_idx, *cu_->dex_file);
}
}
} else {
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 8c70b5c757..c510b528ff 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -195,7 +195,7 @@ class ClassInitCheckEliminationTest : public testing::Test {
cu_.mir_graph->SSATransformationEnd();
bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
ASSERT_TRUE(gate_result);
- RepeatingTopologicalSortIterator iterator(cu_.mir_graph.get());
+ LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get());
bool change = false;
for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
change = cu_.mir_graph->EliminateClassInitChecks(bb);
@@ -373,30 +373,47 @@ TEST_F(ClassInitCheckEliminationTest, Catch) {
static const SFieldDef sfields[] = {
{ 0u, 1u, 0u, 0u },
{ 1u, 1u, 1u, 1u },
+ { 2u, 1u, 2u, 2u },
+ { 3u, 1u, 3u, 3u },
};
static const BBDef bbs[] = {
DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
- DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
- DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED1(1)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(5), DEF_PRED1(3)), // Catch handler.
- DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(3, 4)),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block.
};
static const MIRDef mirs[] = {
- DEF_MIR(Instruction::SGET, 3u, 0u),
- DEF_MIR(Instruction::SGET, 3u, 1u),
- DEF_MIR(Instruction::SGET, 4u, 1u),
- DEF_MIR(Instruction::SGET, 5u, 0u), // Not eliminated.
- DEF_MIR(Instruction::SGET, 5u, 1u), // Eliminated.
+ DEF_MIR(Instruction::SGET, 3u, 0u), // Before the exception edge.
+ DEF_MIR(Instruction::SGET, 3u, 1u), // Before the exception edge.
+ DEF_MIR(Instruction::SGET, 4u, 2u), // After the exception edge.
+ DEF_MIR(Instruction::SGET, 4u, 3u), // After the exception edge.
+ DEF_MIR(Instruction::SGET, 5u, 0u), // In catch handler; class init check eliminated.
+ DEF_MIR(Instruction::SGET, 5u, 2u), // In catch handler; class init check not eliminated.
+ DEF_MIR(Instruction::SGET, 6u, 0u), // Class init check eliminated.
+ DEF_MIR(Instruction::SGET, 6u, 1u), // Class init check eliminated.
+ DEF_MIR(Instruction::SGET, 6u, 2u), // Class init check eliminated.
+ DEF_MIR(Instruction::SGET, 6u, 3u), // Class init check not eliminated.
};
static const bool expected_ignore_clinit_check[] = {
- false, false, false, false, true
+ false, false, false, false, true, false, true, true, true, false
};
PrepareSFields(sfields);
PrepareBasicBlocks(bbs);
- BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(4u);
+ BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
catch_handler->catch_entry = true;
+ // Add successor block info to the check block.
+ BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
+ check_bb->successor_block_list_type = kCatch;
+ check_bb->successor_blocks = new (&cu_.arena) GrowableArray<SuccessorBlockInfo*>(
+ &cu_.arena, 2, kGrowableArraySuccessorBlocks);
+ SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
+ (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+ successor_block_info->block = catch_handler->id;
+ check_bb->successor_blocks->Insert(successor_block_info);
PrepareMIRs(mirs);
PerformClassInitCheckElimination();
ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h
index 031c5cf87d..133593ceb9 100644
--- a/compiler/dex/pass_driver_me.h
+++ b/compiler/dex/pass_driver_me.h
@@ -68,6 +68,9 @@ class PassDriverME: public PassDriver<PassDriverType> {
case kRepeatingTopologicalSortTraversal:
DoWalkBasicBlocks<RepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass);
break;
+ case kLoopRepeatingTopologicalSortTraversal:
+ DoWalkBasicBlocks<LoopRepeatingTopologicalSortIterator>(&pass_me_data_holder_, me_pass);
+ break;
case kAllNodes:
DoWalkBasicBlocks<AllNodesIterator>(&pass_me_data_holder_, me_pass);
break;
diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h
index ff698654cf..c7276eb905 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -55,6 +55,7 @@ enum DataFlowAnalysisMode {
kPostOrderDOMTraversal, /**< @brief Dominator tree / Post-Order. */
kTopologicalSortTraversal, /**< @brief Topological Order traversal. */
kRepeatingTopologicalSortTraversal, /**< @brief Repeating Topological Order traversal. */
+ kLoopRepeatingTopologicalSortTraversal, /**< @brief Loop-repeating Topological Order traversal. */
kNoNodes, /**< @brief Skip BasicBlock traversal. */
};
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 6b96e929aa..5059c5fc9a 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -358,7 +358,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
*/
bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm);
NewLIR0(kPseudoMethodEntry);
- constexpr size_t kStackOverflowReservedUsableBytes = kArmStackOverflowReservedBytes -
+ const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm) -
Thread::kStackOverflowSignalReservedBytes;
bool large_frame = (static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes);
if (!skip_overflow_check) {
@@ -381,7 +381,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
// This is done before the callee save instructions to avoid any possibility
// of these overflowing. This uses r12 and that's never saved in a callee
// save.
- OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, kArmStackOverflowReservedBytes);
+ OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, GetStackOverflowReservedBytes(kArm));
Load32Disp(rs_r12, 0, rs_r12);
MarkPossibleStackOverflowException();
}
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 582af510f3..fa252a14a5 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -110,6 +110,8 @@ class ArmMir2Lir FINAL : public Mir2Lir {
void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
+ bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
bool GenInlinedSqrt(CallInfo* info);
@@ -136,6 +138,9 @@ class ArmMir2Lir FINAL : public Mir2Lir {
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) OVERRIDE;
bool GenMemBarrier(MemBarrierKind barrier_kind);
void GenMonitorEnter(int opt_flags, RegLocation rl_src);
void GenMonitorExit(int opt_flags, RegLocation rl_src);
diff --git a/compiler/dex/quick/arm/fp_arm.cc b/compiler/dex/quick/arm/fp_arm.cc
index e06d814e16..dcb88570c0 100644
--- a/compiler/dex/quick/arm/fp_arm.cc
+++ b/compiler/dex/quick/arm/fp_arm.cc
@@ -338,6 +338,60 @@ void ArmMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
StoreValueWide(rl_dest, rl_result);
}
+static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
+ // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
+ if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
+ (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
+ return kCoreReg;
+ }
+ // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
+ if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
+ return kFPReg;
+ }
+ // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
+ // is faster using a core reg or fp reg depends on the particular CPU. Without further
+ // investigation and testing we prefer core register. (If the result is subsequently used in
+ // another fp operation, the dalvik reg will probably get promoted and that should be handled
+ // by the cases above.)
+ return kCoreReg;
+}
+
+bool ArmMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLoc(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValue(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(kThumb2Vabss, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
+bool ArmMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLocWide(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValueWide(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(kThumb2Vabsd, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff);
+ }
+ StoreValueWide(rl_dest, rl_result);
+ return true;
+}
+
bool ArmMir2Lir::GenInlinedSqrt(CallInfo* info) {
DCHECK_EQ(cu_->instruction_set, kThumb2);
RegLocation rl_src = info->args[0];
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 2fcc3a5abc..a85b74021b 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -203,6 +203,30 @@ void ArmMir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
OpCmpImmBranch(ccode, low_reg, val_lo, taken);
}
+void ArmMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) {
+ // TODO: Generalize the IT below to accept more than one-instruction loads.
+ DCHECK(InexpensiveConstantInt(true_val));
+ DCHECK(InexpensiveConstantInt(false_val));
+
+ if ((true_val == 0 && code == kCondEq) ||
+ (false_val == 0 && code == kCondNe)) {
+ OpRegRegReg(kOpSub, rs_dest, left_op, right_op);
+ DCHECK(last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
+ LIR* it = OpIT(kCondNe, "");
+ LoadConstant(rs_dest, code == kCondEq ? false_val : true_val);
+ OpEndIT(it);
+ return;
+ }
+
+ OpRegReg(kOpCmp, left_op, right_op); // Same?
+ LIR* it = OpIT(code, "E"); // if-convert the test
+ LoadConstant(rs_dest, true_val); // .eq case - load true
+ LoadConstant(rs_dest, false_val); // .eq case - load true
+ OpEndIT(it);
+}
+
void ArmMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
RegLocation rl_result;
RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc
index 9cbf7b89f2..9bb9dda187 100644
--- a/compiler/dex/quick/arm/utility_arm.cc
+++ b/compiler/dex/quick/arm/utility_arm.cc
@@ -1107,7 +1107,7 @@ LIR* ArmMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r
// take 4, we can't directly allocate 2 more for LDREXD temps. In that case clobber r_ptr
// in LDREXD and recalculate it from r_base.
RegStorage r_temp = AllocTemp();
- RegStorage r_temp_high = AllocFreeTemp(); // We may not have another temp.
+ RegStorage r_temp_high = AllocTemp(false); // We may not have another temp.
if (r_temp_high.Valid()) {
NewLIR3(kThumb2Ldrexd, r_temp.GetReg(), r_temp_high.GetReg(), r_ptr.GetReg());
FreeTemp(r_temp_high);
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index d946ee39ef..5e95500b25 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -330,8 +330,8 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method)
NewLIR0(kPseudoMethodEntry);
- constexpr size_t kStackOverflowReservedUsableBytes = kArm64StackOverflowReservedBytes -
- Thread::kStackOverflowSignalReservedBytes;
+ const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64) -
+ Thread::kStackOverflowSignalReservedBytes;
const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes;
const int spill_count = num_core_spills_ + num_fp_spills_;
const int spill_size = (spill_count * kArm64PointerSize + 15) & ~0xf; // SP 16 byte alignment.
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index f51145cfe9..8d15326827 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -175,6 +175,7 @@ class Arm64Mir2Lir FINAL : public Mir2Lir {
RegLocation rl_src2);
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
bool GenInlinedReverseBits(CallInfo* info, OpSize size);
+ bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
@@ -204,7 +205,14 @@ class Arm64Mir2Lir FINAL : public Mir2Lir {
void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
- void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
+ void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) OVERRIDE;
+ // Helper used in the above two.
+ void GenSelect(int32_t left, int32_t right, ConditionCode code, RegStorage rs_dest,
+ int result_reg_class);
+
bool GenMemBarrier(MemBarrierKind barrier_kind);
void GenMonitorEnter(int opt_flags, RegLocation rl_src);
void GenMonitorExit(int opt_flags, RegLocation rl_src);
diff --git a/compiler/dex/quick/arm64/fp_arm64.cc b/compiler/dex/quick/arm64/fp_arm64.cc
index 6594c4b7a7..175cef1859 100644
--- a/compiler/dex/quick/arm64/fp_arm64.cc
+++ b/compiler/dex/quick/arm64/fp_arm64.cc
@@ -323,12 +323,57 @@ void Arm64Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) {
StoreValueWide(rl_dest, rl_result);
}
+static RegisterClass RegClassForAbsFP(RegLocation rl_src, RegLocation rl_dest) {
+ // If src is in a core reg or, unlikely, dest has been promoted to a core reg, use core reg.
+ if ((rl_src.location == kLocPhysReg && !rl_src.reg.IsFloat()) ||
+ (rl_dest.location == kLocPhysReg && !rl_dest.reg.IsFloat())) {
+ return kCoreReg;
+ }
+ // If src is in an fp reg or dest has been promoted to an fp reg, use fp reg.
+ if (rl_src.location == kLocPhysReg || rl_dest.location == kLocPhysReg) {
+ return kFPReg;
+ }
+ // With both src and dest in the stack frame we have to perform load+abs+store. Whether this
+ // is faster using a core reg or fp reg depends on the particular CPU. For example, on A53
+ // it's faster using core reg while on A57 it's faster with fp reg, the difference being
+ // bigger on the A53. Without further investigation and testing we prefer core register.
+ // (If the result is subsequently used in another fp operation, the dalvik reg will probably
+ // get promoted and that should be handled by the cases above.)
+ return kCoreReg;
+}
+
+bool Arm64Mir2Lir::GenInlinedAbsFloat(CallInfo* info) {
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLoc(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValue(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(kA64Fabs2ff, rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ NewLIR4(kA64Ubfm4rrdd, rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 30);
+ }
+ StoreValue(rl_dest, rl_result);
+ return true;
+}
+
bool Arm64Mir2Lir::GenInlinedAbsDouble(CallInfo* info) {
- RegLocation rl_src = info->args[0];
- rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- NewLIR4(WIDE(kA64Ubfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 62);
+ if (info->result.location == kLocInvalid) {
+ return true; // Result is unused: inlining successful, no code generated.
+ }
+ RegLocation rl_dest = info->result;
+ RegLocation rl_src = UpdateLocWide(info->args[0]);
+ RegisterClass reg_class = RegClassForAbsFP(rl_src, rl_dest);
+ rl_src = LoadValueWide(rl_src, reg_class);
+ RegLocation rl_result = EvalLoc(rl_dest, reg_class, true);
+ if (reg_class == kFPReg) {
+ NewLIR2(FWIDE(kA64Fabs2ff), rl_result.reg.GetReg(), rl_src.reg.GetReg());
+ } else {
+ NewLIR4(WIDE(kA64Ubfm4rrdd), rl_result.reg.GetReg(), rl_src.reg.GetReg(), 0, 62);
+ }
StoreValueWide(rl_dest, rl_result);
return true;
}
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 2b78e81f46..aed8de8191 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -85,141 +85,129 @@ void Arm64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
StoreValueWide(rl_dest, rl_result);
}
-void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
- RegLocation rl_result;
- RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
- RegLocation rl_dest = mir_graph_->GetDest(mir);
- RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
- RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
+static constexpr bool kUseDeltaEncodingInGenSelect = false;
+
+void Arm64Mir2Lir::GenSelect(int32_t true_val, int32_t false_val, ConditionCode ccode,
+ RegStorage rs_dest, int result_reg_class) {
+ if (false_val == 0 || // 0 is better as first operand.
+ true_val == 1 || // Potentially Csinc.
+ true_val == -1 || // Potentially Csinv.
+ true_val == false_val + 1) { // Potentially Csinc.
+ ccode = NegateComparison(ccode);
+ std::swap(true_val, false_val);
+ }
- rl_src = LoadValue(rl_src, src_reg_class);
- // rl_src may be aliased with rl_result/rl_dest, so do compare early.
- OpRegImm(kOpCmp, rl_src.reg, 0);
+ ArmConditionCode code = ArmConditionEncoding(ccode);
- ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode);
+ int opcode; // The opcode.
+ RegStorage left_op = RegStorage::InvalidReg(); // The operands.
+ RegStorage right_op = RegStorage::InvalidReg(); // The operands.
- // The kMirOpSelect has two variants, one for constants and one for moves.
- bool is_wide = rl_dest.ref || rl_dest.wide;
+ bool is_wide = rs_dest.Is64Bit();
- if (mir->ssa_rep->num_uses == 1) {
- uint32_t true_val = mir->dalvikInsn.vB;
- uint32_t false_val = mir->dalvikInsn.vC;
-
- int opcode; // The opcode.
- int left_op, right_op; // The operands.
- bool rl_result_evaled = false;
-
- // Check some simple cases.
- // TODO: Improve this.
- int zero_reg = (is_wide ? rs_xzr : rs_wzr).GetReg();
-
- if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) {
- // CSInc cheap based on wzr.
- if (true_val == 1) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
+ RegStorage zero_reg = is_wide ? rs_xzr : rs_wzr;
- left_op = right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
- } else if ((true_val == 0 && false_val == 0xFFFFFFFF) ||
- (true_val == 0xFFFFFFFF && false_val == 0)) {
- // CSneg cheap based on wzr.
- if (true_val == 0xFFFFFFFF) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
-
- left_op = right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
- } else if (true_val == 0 || false_val == 0) {
- // Csel half cheap based on wzr.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- if (false_val == 0) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
- LoadConstantNoClobber(rl_result.reg, true_val == 0 ? false_val : true_val);
- left_op = zero_reg;
- right_op = rl_result.reg.GetReg();
- opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
- } else if (true_val == 1 || false_val == 1) {
- // CSInc half cheap based on wzr.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- if (true_val == 1) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
- LoadConstantNoClobber(rl_result.reg, true_val == 1 ? false_val : true_val);
- left_op = rl_result.reg.GetReg();
- right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
- } else if (true_val == 0xFFFFFFFF || false_val == 0xFFFFFFFF) {
- // CSneg half cheap based on wzr.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- if (true_val == 0xFFFFFFFF) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
- LoadConstantNoClobber(rl_result.reg, true_val == 0xFFFFFFFF ? false_val : true_val);
- left_op = rl_result.reg.GetReg();
- right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
- } else if ((true_val + 1 == false_val) || (false_val + 1 == true_val)) {
- // Load a constant and use CSinc. Use rl_result.
- if (false_val + 1 == true_val) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- true_val = false_val;
+ if (true_val == 0) {
+ left_op = zero_reg;
+ } else {
+ left_op = rs_dest;
+ LoadConstantNoClobber(rs_dest, true_val);
+ }
+ if (false_val == 1) {
+ right_op = zero_reg;
+ opcode = kA64Csinc4rrrc;
+ } else if (false_val == -1) {
+ right_op = zero_reg;
+ opcode = kA64Csinv4rrrc;
+ } else if (false_val == true_val + 1) {
+ right_op = left_op;
+ opcode = kA64Csinc4rrrc;
+ } else if (false_val == -true_val) {
+ right_op = left_op;
+ opcode = kA64Csneg4rrrc;
+ } else if (false_val == ~true_val) {
+ right_op = left_op;
+ opcode = kA64Csinv4rrrc;
+ } else if (true_val == 0) {
+ // left_op is zero_reg.
+ right_op = rs_dest;
+ LoadConstantNoClobber(rs_dest, false_val);
+ opcode = kA64Csel4rrrc;
+ } else {
+ // Generic case.
+ RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
+ if (is_wide) {
+ if (t_reg2.Is32Bit()) {
+ t_reg2 = As64BitReg(t_reg2);
}
-
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- LoadConstantNoClobber(rl_result.reg, true_val);
- left_op = right_op = rl_result.reg.GetReg();
- opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
} else {
- // Csel. The rest. Use rl_result and a temp.
- // TODO: To minimize the constants being loaded, check whether one can be inexpensively
- // loaded as n - 1 or ~n.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- LoadConstantNoClobber(rl_result.reg, true_val);
- RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
- if (rl_dest.wide) {
- if (t_reg2.Is32Bit()) {
- t_reg2 = As64BitReg(t_reg2);
- }
+ if (t_reg2.Is64Bit()) {
+ t_reg2 = As32BitReg(t_reg2);
}
- LoadConstantNoClobber(t_reg2, false_val);
+ }
- // Use csel.
- left_op = rl_result.reg.GetReg();
- right_op = t_reg2.GetReg();
- opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
+ if (kUseDeltaEncodingInGenSelect) {
+ int32_t delta = false_val - true_val;
+ uint32_t abs_val = delta < 0 ? -delta : delta;
+
+ if (abs_val < 0x1000) { // TODO: Replace with InexpensiveConstant with opcode.
+ // Can encode as immediate to an add.
+ right_op = t_reg2;
+ OpRegRegImm(kOpAdd, t_reg2, left_op, delta);
+ }
}
- if (!rl_result_evaled) {
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ // Load as constant.
+ if (!right_op.Valid()) {
+ LoadConstantNoClobber(t_reg2, false_val);
+ right_op = t_reg2;
}
- NewLIR4(opcode, rl_result.reg.GetReg(), left_op, right_op, code);
+ opcode = kA64Csel4rrrc;
+ }
+
+ DCHECK(left_op.Valid() && right_op.Valid());
+ NewLIR4(is_wide ? WIDE(opcode) : opcode, rs_dest.GetReg(), left_op.GetReg(), right_op.GetReg(),
+ code);
+}
+
+void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) {
+ DCHECK(rs_dest.Valid());
+ OpRegReg(kOpCmp, left_op, right_op);
+ GenSelect(true_val, false_val, code, rs_dest, dest_reg_class);
+}
+
+void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+ RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg);
+ // rl_src may be aliased with rl_result/rl_dest, so do compare early.
+ OpRegImm(kOpCmp, rl_src.reg, 0);
+
+ RegLocation rl_dest = mir_graph_->GetDest(mir);
+
+ // The kMirOpSelect has two variants, one for constants and one for moves.
+ if (mir->ssa_rep->num_uses == 1) {
+ RegLocation rl_result = EvalLoc(rl_dest, rl_dest.ref ? kRefReg : kCoreReg, true);
+ GenSelect(mir->dalvikInsn.vB, mir->dalvikInsn.vC, mir->meta.ccode, rl_result.reg,
+ rl_dest.ref ? kRefReg : kCoreReg);
+ StoreValue(rl_dest, rl_result);
} else {
RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
+ RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
rl_true = LoadValue(rl_true, result_reg_class);
rl_false = LoadValue(rl_false, result_reg_class);
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ RegLocation rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ bool is_wide = rl_dest.ref || rl_dest.wide;
int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
NewLIR4(opcode, rl_result.reg.GetReg(),
- rl_true.reg.GetReg(), rl_false.reg.GetReg(), code);
+ rl_true.reg.GetReg(), rl_false.reg.GetReg(), ArmConditionEncoding(mir->meta.ccode));
+ StoreValue(rl_dest, rl_result);
}
- StoreValue(rl_dest, rl_result);
}
void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {
diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc
index 502859a72b..adc228cf45 100644
--- a/compiler/dex/quick/gen_common.cc
+++ b/compiler/dex/quick/gen_common.cc
@@ -1166,7 +1166,6 @@ void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, Re
LoadRefDisp(check_class, offset_of_type, check_class, kNotVolatile);
}
- LIR* ne_branchover = NULL;
// FIXME: what should we be comparing here? compressed or decompressed references?
if (cu_->instruction_set == kThumb2) {
OpRegReg(kOpCmp, check_class, object_class); // Same?
@@ -1174,14 +1173,10 @@ void Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, Re
LoadConstant(result_reg, 1); // .eq case - load true
OpEndIT(it);
} else {
- ne_branchover = OpCmpBranch(kCondNe, check_class, object_class, NULL);
- LoadConstant(result_reg, 1); // eq case - load true
+ GenSelectConst32(check_class, object_class, kCondEq, 1, 0, result_reg, kCoreReg);
}
LIR* target = NewLIR0(kPseudoTargetLabel);
null_branchover->target = target;
- if (ne_branchover != NULL) {
- ne_branchover->target = target;
- }
FreeTemp(object_class);
FreeTemp(check_class);
if (IsTemp(result_reg)) {
@@ -1196,15 +1191,14 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know
bool can_assume_type_is_in_dex_cache,
uint32_t type_idx, RegLocation rl_dest,
RegLocation rl_src) {
- // X86 has its own implementation.
- DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64);
-
FlushAllRegs();
// May generate a call - use explicit registers
LockCallTemps();
RegStorage method_reg = TargetReg(kArg1, kRef);
LoadCurrMethodDirect(method_reg); // kArg1 <= current Method*
RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class*
+ RegStorage ref_reg = TargetReg(kArg0, kRef); // kArg0 will hold the ref.
+ RegStorage ret_reg = GetReturn(kRefReg).reg;
if (needs_access_check) {
// Check we have access to type_idx and if not throw IllegalAccessError,
// returns Class* in kArg0
@@ -1215,63 +1209,82 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know
CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeTypeAndVerifyAccess),
type_idx, true);
}
- OpRegCopy(class_reg, TargetReg(kRet0, kRef)); // Align usage with fast path
- LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref
+ OpRegCopy(class_reg, ret_reg); // Align usage with fast path
+ LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
} else if (use_declaring_class) {
- LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref
+ LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
class_reg, kNotVolatile);
} else {
+ if (can_assume_type_is_in_dex_cache) {
+ // Conditionally, as in the other case we will also load it.
+ LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
+ }
+
// Load dex cache entry into class_reg (kArg2)
- LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); // kArg0 <= ref
LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
class_reg, kNotVolatile);
int32_t offset_of_type = ClassArray::OffsetOfElement(type_idx).Int32Value();
LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
if (!can_assume_type_is_in_dex_cache) {
- // Need to test presence of type in dex cache at runtime
- LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
- // Not resolved
- // Call out to helper, which will return resolved type in kRet0
- if (cu_->target64) {
- CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(8, pInitializeType), type_idx, true);
- } else {
- CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx, true);
- }
- OpRegCopy(TargetReg(kArg2, kRef), TargetReg(kRet0, kRef)); // Align usage with fast path
- LoadValueDirectFixed(rl_src, TargetReg(kArg0, kRef)); /* reload Ref */
- // Rejoin code paths
- LIR* hop_target = NewLIR0(kPseudoTargetLabel);
- hop_branch->target = hop_target;
+ LIR* slow_path_branch = OpCmpImmBranch(kCondEq, class_reg, 0, NULL);
+ LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
+
+ // Should load value here.
+ LoadValueDirectFixed(rl_src, ref_reg); // kArg0 <= ref
+
+ class InitTypeSlowPath : public Mir2Lir::LIRSlowPath {
+ public:
+ InitTypeSlowPath(Mir2Lir* m2l, LIR* branch, LIR* cont, uint32_t type_idx,
+ RegLocation rl_src)
+ : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, cont), type_idx_(type_idx),
+ rl_src_(rl_src) {
+ }
+
+ void Compile() OVERRIDE {
+ GenerateTargetLabel();
+
+ if (cu_->target64) {
+ m2l_->CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(8, pInitializeType), type_idx_,
+ true);
+ } else {
+ m2l_->CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx_,
+ true);
+ }
+ m2l_->OpRegCopy(m2l_->TargetReg(kArg2, kRef),
+ m2l_->TargetReg(kRet0, kRef)); // Align usage with fast path
+
+ m2l_->OpUnconditionalBranch(cont_);
+ }
+
+ private:
+ uint32_t type_idx_;
+ RegLocation rl_src_;
+ };
+
+ AddSlowPath(new (arena_) InitTypeSlowPath(this, slow_path_branch, slow_path_target,
+ type_idx, rl_src));
}
}
/* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result */
RegLocation rl_result = GetReturn(kCoreReg);
- if (cu_->instruction_set == kMips) {
- // On MIPS rArg0 != rl_result, place false in result if branch is taken.
+ if (!IsSameReg(rl_result.reg, ref_reg)) {
+ // On MIPS and x86_64 rArg0 != rl_result, place false in result if branch is taken.
LoadConstant(rl_result.reg, 0);
}
- LIR* branch1 = OpCmpImmBranch(kCondEq, TargetReg(kArg0, kRef), 0, NULL);
+ LIR* branch1 = OpCmpImmBranch(kCondEq, ref_reg, 0, NULL);
/* load object->klass_ */
+ RegStorage ref_class_reg = TargetReg(kArg1, kRef); // kArg1 will hold the Class* of ref.
DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
- LoadRefDisp(TargetReg(kArg0, kRef), mirror::Object::ClassOffset().Int32Value(),
- TargetReg(kArg1, kRef), kNotVolatile);
+ LoadRefDisp(ref_reg, mirror::Object::ClassOffset().Int32Value(),
+ ref_class_reg, kNotVolatile);
/* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class */
LIR* branchover = NULL;
if (type_known_final) {
- // rl_result == ref == null == 0.
- if (cu_->instruction_set == kThumb2) {
- OpRegReg(kOpCmp, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef)); // Same?
- LIR* it = OpIT(kCondEq, "E"); // if-convert the test
- LoadConstant(rl_result.reg, 1); // .eq case - load true
- LoadConstant(rl_result.reg, 0); // .ne case - load false
- OpEndIT(it);
- } else {
- LoadConstant(rl_result.reg, 0); // ne case - load false
- branchover = OpCmpBranch(kCondNe, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), NULL);
- LoadConstant(rl_result.reg, 1); // eq case - load true
- }
+ // rl_result == ref == class.
+ GenSelectConst32(ref_class_reg, class_reg, kCondEq, 1, 0, rl_result.reg,
+ kCoreReg);
} else {
if (cu_->instruction_set == kThumb2) {
RegStorage r_tgt = cu_->target64 ?
@@ -1280,11 +1293,11 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know
LIR* it = nullptr;
if (!type_known_abstract) {
/* Uses conditional nullification */
- OpRegReg(kOpCmp, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef)); // Same?
+ OpRegReg(kOpCmp, ref_class_reg, class_reg); // Same?
it = OpIT(kCondEq, "EE"); // if-convert the test
- LoadConstant(TargetReg(kArg0, kNotWide), 1); // .eq case - load true
+ LoadConstant(rl_result.reg, 1); // .eq case - load true
}
- OpRegCopy(TargetReg(kArg0, kRef), TargetReg(kArg2, kRef)); // .ne case - arg0 <= class
+ OpRegCopy(ref_reg, class_reg); // .ne case - arg0 <= class
OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class)
if (it != nullptr) {
OpEndIT(it);
@@ -1296,12 +1309,13 @@ void Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_know
LoadConstant(rl_result.reg, 1); // assume true
branchover = OpCmpBranch(kCondEq, TargetReg(kArg1, kRef), TargetReg(kArg2, kRef), NULL);
}
- RegStorage r_tgt = cu_->target64 ?
- LoadHelper(QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial)) :
- LoadHelper(QUICK_ENTRYPOINT_OFFSET(4, pInstanceofNonTrivial));
- OpRegCopy(TargetReg(kArg0, kRef), TargetReg(kArg2, kRef)); // .ne case - arg0 <= class
- OpReg(kOpBlx, r_tgt); // .ne case: helper(class, ref->class)
- FreeTemp(r_tgt);
+
+ OpRegCopy(TargetReg(kArg0, kRef), class_reg); // .ne case - arg0 <= class
+ if (cu_->target64) {
+ CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial), false);
+ } else {
+ CallRuntimeHelper(QUICK_ENTRYPOINT_OFFSET(4, pInstanceofNonTrivial), false);
+ }
}
}
// TODO: only clobber when type isn't final?
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index 8ce6e1a206..2c695932f5 100755
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1261,7 +1261,9 @@ bool Mir2Lir::GenInlinedGet(CallInfo* info) {
bool is_finalizable;
const DexFile* old_dex = cu_->dex_file;
cu_->dex_file = ref_dex_file;
- RegStorage reg_class = TargetPtrReg(kArg1);
+ RegStorage reg_class = TargetReg(kArg1, kRef);
+ Clobber(reg_class);
+ LockTemp(reg_class);
if (!cu_->compiler_driver->CanEmbedTypeInCode(*ref_dex_file, type_idx, &unused_type_initialized,
&use_direct_type_ptr, &direct_type_ptr,
&is_finalizable) || is_finalizable) {
@@ -1296,11 +1298,19 @@ bool Mir2Lir::GenInlinedGet(CallInfo* info) {
RegStorage reg_disabled = AllocTemp();
Load32Disp(reg_class, slow_path_flag_offset, reg_slow_path);
Load32Disp(reg_class, disable_flag_offset, reg_disabled);
- OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled);
+ FreeTemp(reg_class);
+ LIR* or_inst = OpRegRegReg(kOpOr, reg_slow_path, reg_slow_path, reg_disabled);
FreeTemp(reg_disabled);
// if slow path, jump to JNI path target
- LIR* slow_path_branch = OpCmpImmBranch(kCondNe, reg_slow_path, 0, nullptr);
+ LIR* slow_path_branch;
+ if (or_inst->u.m.def_mask->HasBit(ResourceMask::kCCode)) {
+ // Generate conditional branch only, as the OR set a condition state (we are interested in a 'Z' flag).
+ slow_path_branch = OpCondBranch(kCondNe, nullptr);
+ } else {
+ // Generate compare and branch.
+ slow_path_branch = OpCmpImmBranch(kCondNe, reg_slow_path, 0, nullptr);
+ }
FreeTemp(reg_slow_path);
// slow path not enabled, simply load the referent of the reference object
@@ -1543,20 +1553,6 @@ bool Mir2Lir::GenInlinedAbsLong(CallInfo* info) {
return true;
}
-bool Mir2Lir::GenInlinedAbsFloat(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
- }
- RegLocation rl_src = info->args[0];
- rl_src = LoadValue(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTarget(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
- OpRegRegImm(kOpAnd, rl_result.reg, rl_src.reg, 0x7fffffff);
- StoreValue(rl_dest, rl_result);
- return true;
-}
-
bool Mir2Lir::GenInlinedReverseBits(CallInfo* info, OpSize size) {
// Currently implemented only for ARM64
return false;
@@ -1567,22 +1563,6 @@ bool Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
return false;
}
-bool Mir2Lir::GenInlinedAbsDouble(CallInfo* info) {
- if (cu_->instruction_set == kMips) {
- // TODO - add Mips implementation
- return false;
- }
- RegLocation rl_src = info->args[0];
- rl_src = LoadValueWide(rl_src, kCoreReg);
- RegLocation rl_dest = InlineTargetWide(info);
- RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
-
- OpRegCopyWide(rl_result.reg, rl_src.reg);
- OpRegImm(kOpAnd, rl_result.reg.GetHigh(), 0x7fffffff);
- StoreValueWide(rl_dest, rl_result);
- return true;
-}
-
bool Mir2Lir::GenInlinedFloatCvt(CallInfo* info) {
if (cu_->instruction_set == kMips) {
// TODO - add Mips implementation
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 4a060863df..bb18ad2b73 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -110,6 +110,8 @@ class MipsMir2Lir FINAL : public Mir2Lir {
void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1,
RegLocation rl_src2);
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
+ bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
+ bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
bool GenInlinedSqrt(CallInfo* info);
@@ -136,6 +138,9 @@ class MipsMir2Lir FINAL : public Mir2Lir {
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) OVERRIDE;
bool GenMemBarrier(MemBarrierKind barrier_kind);
void GenMoveException(RegLocation rl_dest);
void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc
index c3a4c1714c..054514e8ad 100644
--- a/compiler/dex/quick/mips/int_mips.cc
+++ b/compiler/dex/quick/mips/int_mips.cc
@@ -215,6 +215,18 @@ void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
}
}
+void MipsMir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) {
+ // Implement as a branch-over.
+ // TODO: Conditional move?
+ LoadConstant(rs_dest, false_val); // Favors false.
+ LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL);
+ LoadConstant(rs_dest, true_val);
+ LIR* target_label = NewLIR0(kPseudoTargetLabel);
+ ne_branchover->target = target_label;
+}
+
void MipsMir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
UNIMPLEMENTED(FATAL) << "Need codegen for select";
}
@@ -279,6 +291,16 @@ bool MipsMir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) {
return false;
}
+bool MipsMir2Lir::GenInlinedAbsFloat(CallInfo* info) {
+ // TODO - add Mips implementation
+ return false;
+}
+
+bool MipsMir2Lir::GenInlinedAbsDouble(CallInfo* info) {
+ // TODO - add Mips implementation
+ return false;
+}
+
bool MipsMir2Lir::GenInlinedSqrt(CallInfo* info) {
DCHECK_NE(cu_->instruction_set, kThumb2);
return false;
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 5b5663353c..f183dc90a9 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -32,6 +32,7 @@
#include "utils/array_ref.h"
#include "utils/arena_allocator.h"
#include "utils/growable_array.h"
+#include "utils/stack_checks.h"
namespace art {
@@ -205,36 +206,6 @@ Mir2Lir* X86CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph,
#define SLOW_TYPE_PATH (cu_->enable_debug & (1 << kDebugSlowTypePath))
#define EXERCISE_SLOWEST_STRING_PATH (cu_->enable_debug & (1 << kDebugSlowestStringPath))
-// Size of a frame that we definitely consider large. Anything larger than this should
-// definitely get a stack overflow check.
-static constexpr size_t kLargeFrameSize = 2 * KB;
-
-// Size of a frame that should be small. Anything leaf method smaller than this should run
-// without a stack overflow check.
-// The constant is from experience with frameworks code.
-static constexpr size_t kSmallFrameSize = 1 * KB;
-
-// Determine whether a frame is small or large, used in the decision on whether to elide a
-// stack overflow check on method entry.
-//
-// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the
-// overflow-usable stack space.
-static constexpr bool IsLargeFrame(size_t size, InstructionSet isa) {
- return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4;
-}
-
-// We want to ensure that on all systems kSmallFrameSize will lead to false in IsLargeFrame.
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kArm),
- kSmallFrameSize_is_not_a_small_frame_arm);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kArm64),
- kSmallFrameSize_is_not_a_small_frame_arm64);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kMips),
- kSmallFrameSize_is_not_a_small_frame_mips);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kX86),
- kSmallFrameSize_is_not_a_small_frame_x86);
-COMPILE_ASSERT(!IsLargeFrame(kSmallFrameSize, kX86_64),
- kSmallFrameSize_is_not_a_small_frame_x64_64);
-
class Mir2Lir : public Backend {
public:
static constexpr bool kFailOnSizeError = true && kIsDebugBuild;
@@ -745,14 +716,13 @@ class Mir2Lir : public Backend {
virtual RegStorage AllocPreservedSingle(int s_reg);
virtual RegStorage AllocPreservedDouble(int s_reg);
RegStorage AllocTempBody(GrowableArray<RegisterInfo*> &regs, int* next_temp, bool required);
- virtual RegStorage AllocFreeTemp();
- virtual RegStorage AllocTemp();
- virtual RegStorage AllocTempWide();
- virtual RegStorage AllocTempRef();
- virtual RegStorage AllocTempSingle();
- virtual RegStorage AllocTempDouble();
- virtual RegStorage AllocTypedTemp(bool fp_hint, int reg_class);
- virtual RegStorage AllocTypedTempWide(bool fp_hint, int reg_class);
+ virtual RegStorage AllocTemp(bool required = true);
+ virtual RegStorage AllocTempWide(bool required = true);
+ virtual RegStorage AllocTempRef(bool required = true);
+ virtual RegStorage AllocTempSingle(bool required = true);
+ virtual RegStorage AllocTempDouble(bool required = true);
+ virtual RegStorage AllocTypedTemp(bool fp_hint, int reg_class, bool required = true);
+ virtual RegStorage AllocTypedTempWide(bool fp_hint, int reg_class, bool required = true);
void FlushReg(RegStorage reg);
void FlushRegWide(RegStorage reg);
RegStorage AllocLiveReg(int s_reg, int reg_class, bool wide);
@@ -990,8 +960,8 @@ class Mir2Lir : public Backend {
bool GenInlinedReverseBytes(CallInfo* info, OpSize size);
bool GenInlinedAbsInt(CallInfo* info);
virtual bool GenInlinedAbsLong(CallInfo* info);
- virtual bool GenInlinedAbsFloat(CallInfo* info);
- virtual bool GenInlinedAbsDouble(CallInfo* info);
+ virtual bool GenInlinedAbsFloat(CallInfo* info) = 0;
+ virtual bool GenInlinedAbsDouble(CallInfo* info) = 0;
bool GenInlinedFloatCvt(CallInfo* info);
bool GenInlinedDoubleCvt(CallInfo* info);
virtual bool GenInlinedArrayCopyCharArray(CallInfo* info);
@@ -1381,6 +1351,13 @@ class Mir2Lir : public Backend {
virtual void GenSelect(BasicBlock* bb, MIR* mir) = 0;
/**
+ * @brief Generates code to select one of the given constants depending on the given opcode.
+ */
+ virtual void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) = 0;
+
+ /**
* @brief Used to generate a memory barrier in an architecture specific way.
* @details The last generated LIR will be considered for use as barrier. Namely,
* if the last LIR can be updated in a way where it will serve the semantics of
@@ -1536,11 +1513,23 @@ class Mir2Lir : public Backend {
void AddSlowPath(LIRSlowPath* slowpath);
- virtual void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
- bool type_known_abstract, bool use_declaring_class,
- bool can_assume_type_is_in_dex_cache,
- uint32_t type_idx, RegLocation rl_dest,
- RegLocation rl_src);
+ /*
+ *
+ * @brief Implement Set up instanceof a class.
+ * @param needs_access_check 'true' if we must check the access.
+ * @param type_known_final 'true' if the type is known to be a final class.
+ * @param type_known_abstract 'true' if the type is known to be an abstract class.
+ * @param use_declaring_class 'true' if the type can be loaded off the current Method*.
+ * @param can_assume_type_is_in_dex_cache 'true' if the type is known to be in the cache.
+ * @param type_idx Type index to use if use_declaring_class is 'false'.
+ * @param rl_dest Result to be set to 0 or 1.
+ * @param rl_src Object to be tested.
+ */
+ void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
+ bool type_known_abstract, bool use_declaring_class,
+ bool can_assume_type_is_in_dex_cache,
+ uint32_t type_idx, RegLocation rl_dest,
+ RegLocation rl_src);
/*
* @brief Generate the debug_frame FDE information if possible.
* @returns pointer to vector containg CFE information, or NULL.
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index fa1c36eaa6..45244e1a7a 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -408,64 +408,67 @@ RegStorage Mir2Lir::AllocTempBody(GrowableArray<RegisterInfo*> &regs, int* next_
return RegStorage::InvalidReg(); // No register available
}
-/* Return a temp if one is available, -1 otherwise */
-RegStorage Mir2Lir::AllocFreeTemp() {
- return AllocTempBody(reg_pool_->core_regs_, &reg_pool_->next_core_reg_, false);
+RegStorage Mir2Lir::AllocTemp(bool required) {
+ return AllocTempBody(reg_pool_->core_regs_, &reg_pool_->next_core_reg_, required);
}
-RegStorage Mir2Lir::AllocTemp() {
- return AllocTempBody(reg_pool_->core_regs_, &reg_pool_->next_core_reg_, true);
-}
-
-RegStorage Mir2Lir::AllocTempWide() {
+RegStorage Mir2Lir::AllocTempWide(bool required) {
RegStorage res;
if (reg_pool_->core64_regs_.Size() != 0) {
- res = AllocTempBody(reg_pool_->core64_regs_, &reg_pool_->next_core64_reg_, true);
+ res = AllocTempBody(reg_pool_->core64_regs_, &reg_pool_->next_core64_reg_, required);
} else {
RegStorage low_reg = AllocTemp();
RegStorage high_reg = AllocTemp();
res = RegStorage::MakeRegPair(low_reg, high_reg);
}
- CheckRegStorage(res, WidenessCheck::kCheckWide, RefCheck::kIgnoreRef, FPCheck::kCheckNotFP);
+ if (required) {
+ CheckRegStorage(res, WidenessCheck::kCheckWide, RefCheck::kIgnoreRef, FPCheck::kCheckNotFP);
+ }
return res;
}
-RegStorage Mir2Lir::AllocTempRef() {
- RegStorage res = AllocTempBody(*reg_pool_->ref_regs_, reg_pool_->next_ref_reg_, true);
- DCHECK(!res.IsPair());
- CheckRegStorage(res, WidenessCheck::kCheckNotWide, RefCheck::kCheckRef, FPCheck::kCheckNotFP);
+RegStorage Mir2Lir::AllocTempRef(bool required) {
+ RegStorage res = AllocTempBody(*reg_pool_->ref_regs_, reg_pool_->next_ref_reg_, required);
+ if (required) {
+ DCHECK(!res.IsPair());
+ CheckRegStorage(res, WidenessCheck::kCheckNotWide, RefCheck::kCheckRef, FPCheck::kCheckNotFP);
+ }
return res;
}
-RegStorage Mir2Lir::AllocTempSingle() {
- RegStorage res = AllocTempBody(reg_pool_->sp_regs_, &reg_pool_->next_sp_reg_, true);
- DCHECK(res.IsSingle()) << "Reg: 0x" << std::hex << res.GetRawBits();
- CheckRegStorage(res, WidenessCheck::kCheckNotWide, RefCheck::kCheckNotRef, FPCheck::kIgnoreFP);
+RegStorage Mir2Lir::AllocTempSingle(bool required) {
+ RegStorage res = AllocTempBody(reg_pool_->sp_regs_, &reg_pool_->next_sp_reg_, required);
+ if (required) {
+ DCHECK(res.IsSingle()) << "Reg: 0x" << std::hex << res.GetRawBits();
+ CheckRegStorage(res, WidenessCheck::kCheckNotWide, RefCheck::kCheckNotRef, FPCheck::kIgnoreFP);
+ }
return res;
}
-RegStorage Mir2Lir::AllocTempDouble() {
- RegStorage res = AllocTempBody(reg_pool_->dp_regs_, &reg_pool_->next_dp_reg_, true);
- DCHECK(res.IsDouble()) << "Reg: 0x" << std::hex << res.GetRawBits();
- CheckRegStorage(res, WidenessCheck::kCheckWide, RefCheck::kCheckNotRef, FPCheck::kIgnoreFP);
+RegStorage Mir2Lir::AllocTempDouble(bool required) {
+ RegStorage res = AllocTempBody(reg_pool_->dp_regs_, &reg_pool_->next_dp_reg_, required);
+ if (required) {
+ DCHECK(res.IsDouble()) << "Reg: 0x" << std::hex << res.GetRawBits();
+ CheckRegStorage(res, WidenessCheck::kCheckWide, RefCheck::kCheckNotRef, FPCheck::kIgnoreFP);
+ }
return res;
}
-RegStorage Mir2Lir::AllocTypedTempWide(bool fp_hint, int reg_class) {
+RegStorage Mir2Lir::AllocTypedTempWide(bool fp_hint, int reg_class, bool required) {
DCHECK_NE(reg_class, kRefReg); // NOTE: the Dalvik width of a reference is always 32 bits.
if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
- return AllocTempDouble();
+ return AllocTempDouble(required);
}
- return AllocTempWide();
+ return AllocTempWide(required);
}
-RegStorage Mir2Lir::AllocTypedTemp(bool fp_hint, int reg_class) {
+RegStorage Mir2Lir::AllocTypedTemp(bool fp_hint, int reg_class, bool required) {
if (((reg_class == kAnyReg) && fp_hint) || (reg_class == kFPReg)) {
- return AllocTempSingle();
+ return AllocTempSingle(required);
} else if (reg_class == kRefReg) {
- return AllocTempRef();
+ return AllocTempRef(required);
}
- return AllocTemp();
+ return AllocTemp(required);
}
RegStorage Mir2Lir::FindLiveReg(GrowableArray<RegisterInfo*> &regs, int s_reg) {
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index ebe3f0a9fc..efd9079ffb 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -367,7 +367,11 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0,
EXT_0F_ENCODING_MAP(Ucomiss, 0x00, 0x2E, SETS_CCODES|REG_USE0),
EXT_0F_ENCODING_MAP(Comisd, 0x66, 0x2F, SETS_CCODES|REG_USE0),
EXT_0F_ENCODING_MAP(Comiss, 0x00, 0x2F, SETS_CCODES|REG_USE0),
+ EXT_0F_ENCODING_MAP(Orpd, 0x66, 0x56, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Orps, 0x00, 0x56, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Andpd, 0x66, 0x54, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Andps, 0x00, 0x54, REG_DEF0_USE0),
+ EXT_0F_ENCODING_MAP(Xorpd, 0x66, 0x57, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Xorps, 0x00, 0x57, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Addsd, 0xF2, 0x58, REG_DEF0_USE0),
EXT_0F_ENCODING_MAP(Addss, 0xF3, 0x58, REG_DEF0_USE0),
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index cf4521ae61..49c0a03e14 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -173,6 +173,7 @@ class X86Mir2Lir : public Mir2Lir {
void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src);
bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object);
bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long);
+ bool GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double);
bool GenInlinedSqrt(CallInfo* info);
bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE;
bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE;
@@ -202,6 +203,9 @@ class X86Mir2Lir : public Mir2Lir {
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) OVERRIDE;
bool GenMemBarrier(MemBarrierKind barrier_kind);
void GenMoveException(RegLocation rl_dest);
void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit,
@@ -267,22 +271,6 @@ class X86Mir2Lir : public Mir2Lir {
*/
void GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, RegLocation rl_dest,
RegLocation rl_src);
- /*
- *
- * @brief Implement Set up instanceof a class with x86 specific code.
- * @param needs_access_check 'true' if we must check the access.
- * @param type_known_final 'true' if the type is known to be a final class.
- * @param type_known_abstract 'true' if the type is known to be an abstract class.
- * @param use_declaring_class 'true' if the type can be loaded off the current Method*.
- * @param can_assume_type_is_in_dex_cache 'true' if the type is known to be in the cache.
- * @param type_idx Type index to use if use_declaring_class is 'false'.
- * @param rl_dest Result to be set to 0 or 1.
- * @param rl_src Object to be tested.
- */
- void GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
- bool type_known_abstract, bool use_declaring_class,
- bool can_assume_type_is_in_dex_cache,
- uint32_t type_idx, RegLocation rl_dest, RegLocation rl_src);
void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_src1, RegLocation rl_shift);
diff --git a/compiler/dex/quick/x86/fp_x86.cc b/compiler/dex/quick/x86/fp_x86.cc
index fc65deb6e5..62053fd97e 100755
--- a/compiler/dex/quick/x86/fp_x86.cc
+++ b/compiler/dex/quick/x86/fp_x86.cc
@@ -705,4 +705,77 @@ bool X86Mir2Lir::GenInlinedAbsDouble(CallInfo* info) {
}
}
+bool X86Mir2Lir::GenInlinedMinMaxFP(CallInfo* info, bool is_min, bool is_double) {
+ if (is_double) {
+ RegLocation rl_src1 = LoadValueWide(info->args[0], kFPReg);
+ RegLocation rl_src2 = LoadValueWide(info->args[2], kFPReg);
+ RegLocation rl_dest = InlineTargetWide(info);
+ RegLocation rl_result = EvalLocWide(rl_dest, kFPReg, true);
+
+ // Avoid src2 corruption by OpRegCopyWide.
+ if (rl_result.reg == rl_src2.reg) {
+ std::swap(rl_src2.reg, rl_src1.reg);
+ }
+
+ OpRegCopyWide(rl_result.reg, rl_src1.reg);
+ NewLIR2(kX86UcomisdRR, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
+ // If either arg is NaN, return NaN.
+ LIR* branch_nan = NewLIR2(kX86Jcc8, 0, kX86CondP);
+ // Min/Max branches.
+ LIR* branch_cond1 = NewLIR2(kX86Jcc8, 0, (is_min) ? kX86CondA : kX86CondB);
+ LIR* branch_cond2 = NewLIR2(kX86Jcc8, 0, (is_min) ? kX86CondB : kX86CondA);
+ // If equal, we need to resolve situations like min/max(0.0, -0.0) == -0.0/0.0.
+ NewLIR2((is_min) ? kX86OrpdRR : kX86AndpdRR, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
+ LIR* branch_exit_equal = NewLIR1(kX86Jmp8, 0);
+ // Handle NaN.
+ branch_nan->target = NewLIR0(kPseudoTargetLabel);
+ LoadConstantWide(rl_result.reg, INT64_C(0x7ff8000000000000));
+ LIR* branch_exit_nan = NewLIR1(kX86Jmp8, 0);
+ // Handle Min/Max. Copy greater/lesser value from src2.
+ branch_cond1->target = NewLIR0(kPseudoTargetLabel);
+ OpRegCopyWide(rl_result.reg, rl_src2.reg);
+ // Right operand is already in result reg.
+ branch_cond2->target = NewLIR0(kPseudoTargetLabel);
+ // Exit.
+ branch_exit_nan->target = NewLIR0(kPseudoTargetLabel);
+ branch_exit_equal->target = NewLIR0(kPseudoTargetLabel);
+ StoreValueWide(rl_dest, rl_result);
+ } else {
+ RegLocation rl_src1 = LoadValue(info->args[0], kFPReg);
+ RegLocation rl_src2 = LoadValue(info->args[1], kFPReg);
+ RegLocation rl_dest = InlineTarget(info);
+ RegLocation rl_result = EvalLoc(rl_dest, kFPReg, true);
+
+ // Avoid src2 corruption by OpRegCopyWide.
+ if (rl_result.reg == rl_src2.reg) {
+ std::swap(rl_src2.reg, rl_src1.reg);
+ }
+
+ OpRegCopy(rl_result.reg, rl_src1.reg);
+ NewLIR2(kX86UcomissRR, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
+ // If either arg is NaN, return NaN.
+ LIR* branch_nan = NewLIR2(kX86Jcc8, 0, kX86CondP);
+ // Min/Max branches.
+ LIR* branch_cond1 = NewLIR2(kX86Jcc8, 0, (is_min) ? kX86CondA : kX86CondB);
+ LIR* branch_cond2 = NewLIR2(kX86Jcc8, 0, (is_min) ? kX86CondB : kX86CondA);
+ // If equal, we need to resolve situations like min/max(0.0, -0.0) == -0.0/0.0.
+ NewLIR2((is_min) ? kX86OrpsRR : kX86AndpsRR, rl_result.reg.GetReg(), rl_src2.reg.GetReg());
+ LIR* branch_exit_equal = NewLIR1(kX86Jmp8, 0);
+ // Handle NaN.
+ branch_nan->target = NewLIR0(kPseudoTargetLabel);
+ LoadConstantNoClobber(rl_result.reg, 0x7fc00000);
+ LIR* branch_exit_nan = NewLIR1(kX86Jmp8, 0);
+ // Handle Min/Max. Copy greater/lesser value from src2.
+ branch_cond1->target = NewLIR0(kPseudoTargetLabel);
+ OpRegCopy(rl_result.reg, rl_src2.reg);
+ // Right operand is already in result reg.
+ branch_cond2->target = NewLIR0(kPseudoTargetLabel);
+ // Exit.
+ branch_exit_nan->target = NewLIR0(kPseudoTargetLabel);
+ branch_exit_equal->target = NewLIR0(kPseudoTargetLabel);
+ StoreValue(rl_dest, rl_result);
+ }
+ return true;
+}
+
} // namespace art
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 3f1df189de..724ee7ed4c 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -206,6 +206,67 @@ void X86Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) {
}
}
+void X86Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) {
+ DCHECK(!left_op.IsPair() && !right_op.IsPair() && !rs_dest.IsPair());
+ DCHECK(!left_op.IsFloat() && !right_op.IsFloat() && !rs_dest.IsFloat());
+
+ // We really need this check for correctness, otherwise we will need to do more checks in
+ // non zero/one case
+ if (true_val == false_val) {
+ LoadConstantNoClobber(rs_dest, true_val);
+ return;
+ }
+
+ const bool dest_intersect = IsSameReg(rs_dest, left_op) || IsSameReg(rs_dest, right_op);
+
+ const bool zero_one_case = (true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0);
+ if (zero_one_case && IsByteRegister(rs_dest)) {
+ if (!dest_intersect) {
+ LoadConstantNoClobber(rs_dest, 0);
+ }
+ OpRegReg(kOpCmp, left_op, right_op);
+ // Set the low byte of the result to 0 or 1 from the compare condition code.
+ NewLIR2(kX86Set8R, rs_dest.GetReg(),
+ X86ConditionEncoding(true_val == 1 ? code : FlipComparisonOrder(code)));
+ if (dest_intersect) {
+ NewLIR2(rs_dest.Is64Bit() ? kX86Movzx8qRR : kX86Movzx8RR, rs_dest.GetReg(), rs_dest.GetReg());
+ }
+ } else {
+ // Be careful rs_dest can be changed only after cmp because it can be the same as one of ops
+ // and it cannot use xor because it makes cc flags to be dirty
+ RegStorage temp_reg = AllocTypedTemp(false, dest_reg_class, false);
+ if (temp_reg.Valid()) {
+ if (false_val == 0 && dest_intersect) {
+ code = FlipComparisonOrder(code);
+ std::swap(true_val, false_val);
+ }
+ if (!dest_intersect) {
+ LoadConstantNoClobber(rs_dest, false_val);
+ }
+ LoadConstantNoClobber(temp_reg, true_val);
+ OpRegReg(kOpCmp, left_op, right_op);
+ if (dest_intersect) {
+ LoadConstantNoClobber(rs_dest, false_val);
+ DCHECK(!last_lir_insn_->u.m.def_mask->HasBit(ResourceMask::kCCode));
+ }
+ OpCondRegReg(kOpCmov, code, rs_dest, temp_reg);
+ FreeTemp(temp_reg);
+ } else {
+ // slow path
+ LIR* cmp_branch = OpCmpBranch(code, left_op, right_op, nullptr);
+ LoadConstantNoClobber(rs_dest, false_val);
+ LIR* that_is_it = NewLIR1(kX86Jmp8, 0);
+ LIR* true_case = NewLIR0(kPseudoTargetLabel);
+ cmp_branch->target = true_case;
+ LoadConstantNoClobber(rs_dest, true_val);
+ LIR* end = NewLIR0(kPseudoTargetLabel);
+ that_is_it->target = end;
+ }
+ }
+}
+
void X86Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
RegLocation rl_result;
RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
@@ -2399,110 +2460,6 @@ void X86Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx,
StoreValue(rl_dest, rl_result);
}
-void X86Mir2Lir::GenInstanceofCallingHelper(bool needs_access_check, bool type_known_final,
- bool type_known_abstract, bool use_declaring_class,
- bool can_assume_type_is_in_dex_cache,
- uint32_t type_idx, RegLocation rl_dest,
- RegLocation rl_src) {
- FlushAllRegs();
- // May generate a call - use explicit registers.
- LockCallTemps();
- RegStorage method_reg = TargetReg(kArg1, kRef); // kArg1 gets current Method*.
- LoadCurrMethodDirect(method_reg);
- RegStorage class_reg = TargetReg(kArg2, kRef); // kArg2 will hold the Class*.
- RegStorage ref_reg = TargetReg(kArg0, kRef); // kArg2 will hold the ref.
- // Reference must end up in kArg0.
- if (needs_access_check) {
- // Check we have access to type_idx and if not throw IllegalAccessError,
- // Caller function returns Class* in kArg0.
- if (cu_->target64) {
- CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(8, pInitializeTypeAndVerifyAccess),
- type_idx, true);
- } else {
- CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeTypeAndVerifyAccess),
- type_idx, true);
- }
- OpRegCopy(class_reg, TargetReg(kRet0, kRef));
- LoadValueDirectFixed(rl_src, ref_reg);
- } else if (use_declaring_class) {
- LoadValueDirectFixed(rl_src, ref_reg);
- LoadRefDisp(method_reg, mirror::ArtMethod::DeclaringClassOffset().Int32Value(),
- class_reg, kNotVolatile);
- } else {
- // Load dex cache entry into class_reg (kArg2).
- LoadValueDirectFixed(rl_src, ref_reg);
- LoadRefDisp(method_reg, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value(),
- class_reg, kNotVolatile);
- int32_t offset_of_type =
- mirror::Array::DataOffset(sizeof(mirror::HeapReference<mirror::Class*>)).Int32Value() +
- (sizeof(mirror::HeapReference<mirror::Class*>) * type_idx);
- LoadRefDisp(class_reg, offset_of_type, class_reg, kNotVolatile);
- if (!can_assume_type_is_in_dex_cache) {
- // Need to test presence of type in dex cache at runtime.
- LIR* hop_branch = OpCmpImmBranch(kCondNe, class_reg, 0, NULL);
- // Type is not resolved. Call out to helper, which will return resolved type in kRet0/kArg0.
- if (cu_->target64) {
- CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(8, pInitializeType), type_idx, true);
- } else {
- CallRuntimeHelperImm(QUICK_ENTRYPOINT_OFFSET(4, pInitializeType), type_idx, true);
- }
- OpRegCopy(class_reg, TargetReg(kRet0, kRef)); // Align usage with fast path.
- LoadValueDirectFixed(rl_src, ref_reg); /* Reload Ref. */
- // Rejoin code paths
- LIR* hop_target = NewLIR0(kPseudoTargetLabel);
- hop_branch->target = hop_target;
- }
- }
- /* kArg0 is ref, kArg2 is class. If ref==null, use directly as bool result. */
- RegLocation rl_result = GetReturn(kRefReg);
-
- // On x86-64 kArg0 is not EAX, so we have to copy ref from kArg0 to EAX.
- if (cu_->target64) {
- OpRegCopy(rl_result.reg, ref_reg);
- }
-
- // For 32-bit, SETcc only works with EAX..EDX.
- DCHECK_LT(rl_result.reg.GetRegNum(), 4);
-
- // Is the class NULL?
- LIR* branch1 = OpCmpImmBranch(kCondEq, ref_reg, 0, NULL);
-
- RegStorage ref_class_reg = TargetReg(kArg1, kRef); // kArg2 will hold the Class*.
- /* Load object->klass_. */
- DCHECK_EQ(mirror::Object::ClassOffset().Int32Value(), 0);
- LoadRefDisp(ref_reg, mirror::Object::ClassOffset().Int32Value(), ref_class_reg,
- kNotVolatile);
- /* kArg0 is ref, kArg1 is ref->klass_, kArg2 is class. */
- LIR* branchover = nullptr;
- if (type_known_final) {
- // Ensure top 3 bytes of result are 0.
- LoadConstant(rl_result.reg, 0);
- OpRegReg(kOpCmp, ref_class_reg, class_reg);
- // Set the low byte of the result to 0 or 1 from the compare condition code.
- NewLIR2(kX86Set8R, rl_result.reg.GetReg(), kX86CondEq);
- } else {
- if (!type_known_abstract) {
- LoadConstant(rl_result.reg, 1); // Assume result succeeds.
- branchover = OpCmpBranch(kCondEq, ref_class_reg, class_reg, NULL);
- }
- OpRegCopy(TargetReg(kArg0, kRef), class_reg);
- if (cu_->target64) {
- OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(8, pInstanceofNonTrivial));
- } else {
- OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(4, pInstanceofNonTrivial));
- }
- }
- // TODO: only clobber when type isn't final?
- ClobberCallerSave();
- /* Branch targets here. */
- LIR* target = NewLIR0(kPseudoTargetLabel);
- StoreValue(rl_dest, rl_result);
- branch1->target = target;
- if (branchover != nullptr) {
- branchover->target = target;
- }
-}
-
void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest,
RegLocation rl_lhs, RegLocation rl_rhs) {
OpKind op = kOpBkpt;
diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc
index 06001d7f9d..451ae8bc19 100755
--- a/compiler/dex/quick/x86/target_x86.cc
+++ b/compiler/dex/quick/x86/target_x86.cc
@@ -1225,19 +1225,12 @@ bool X86Mir2Lir::GenInlinedArrayCopyCharArray(CallInfo* info) {
* otherwise bails to standard library code.
*/
bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
- ClobberCallerSave();
- LockCallTemps(); // Using fixed registers
-
- // EAX: 16 bit character being searched.
- // ECX: count: number of words to be searched.
- // EDI: String being searched.
- // EDX: temporary during execution.
- // EBX or R11: temporary during execution (depending on mode).
-
RegLocation rl_obj = info->args[0];
RegLocation rl_char = info->args[1];
RegLocation rl_start; // Note: only present in III flavor or IndexOf.
- RegStorage tmpReg = cu_->target64 ? rs_r11 : rs_rBX;
+ // RBX is callee-save register in 64-bit mode.
+ RegStorage rs_tmp = cu_->target64 ? rs_r11 : rs_rBX;
+ int start_value = -1;
uint32_t char_value =
rl_char.is_const ? mir_graph_->ConstantValue(rl_char.orig_sreg) : 0;
@@ -1248,22 +1241,46 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
}
// Okay, we are commited to inlining this.
+ // EAX: 16 bit character being searched.
+ // ECX: count: number of words to be searched.
+ // EDI: String being searched.
+ // EDX: temporary during execution.
+ // EBX or R11: temporary during execution (depending on mode).
+ // REP SCASW: search instruction.
+
+ FlushReg(rs_rAX);
+ Clobber(rs_rAX);
+ LockTemp(rs_rAX);
+ FlushReg(rs_rCX);
+ Clobber(rs_rCX);
+ LockTemp(rs_rCX);
+ FlushReg(rs_rDX);
+ Clobber(rs_rDX);
+ LockTemp(rs_rDX);
+ FlushReg(rs_tmp);
+ Clobber(rs_tmp);
+ LockTemp(rs_tmp);
+ if (cu_->target64) {
+ FlushReg(rs_rDI);
+ Clobber(rs_rDI);
+ LockTemp(rs_rDI);
+ }
+
RegLocation rl_return = GetReturn(kCoreReg);
RegLocation rl_dest = InlineTarget(info);
// Is the string non-NULL?
LoadValueDirectFixed(rl_obj, rs_rDX);
GenNullCheck(rs_rDX, info->opt_flags);
- // uint32_t opt_flags = info->opt_flags;
info->opt_flags |= MIR_IGNORE_NULL_CHECK; // Record that we've null checked.
- // Does the character fit in 16 bits?
- LIR* slowpath_branch = nullptr;
+ LIR *slowpath_branch = nullptr, *length_compare = nullptr;
+
+ // We need the value in EAX.
if (rl_char.is_const) {
- // We need the value in EAX.
LoadConstantNoClobber(rs_rAX, char_value);
} else {
- // Character is not a constant; compare at runtime.
+ // Does the character fit in 16 bits? Compare it at runtime.
LoadValueDirectFixed(rl_char, rs_rAX);
slowpath_branch = OpCmpImmBranch(kCondGt, rs_rAX, 0xFFFF, nullptr);
}
@@ -1278,31 +1295,33 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
// Start of char data with array_.
int data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value();
- // Character is in EAX.
- // Object pointer is in EDX.
-
// Compute the number of words to search in to rCX.
Load32Disp(rs_rDX, count_offset, rs_rCX);
- // Possible signal here due to null pointer dereference.
- // Note that the signal handler will expect the top word of
- // the stack to be the ArtMethod*. If the PUSH edi instruction
- // below is ahead of the load above then this will not be true
- // and the signal handler will not work.
- MarkPossibleNullPointerException(0);
+ if (!cu_->target64) {
+ // Possible signal here due to null pointer dereference.
+ // Note that the signal handler will expect the top word of
+ // the stack to be the ArtMethod*. If the PUSH edi instruction
+ // below is ahead of the load above then this will not be true
+ // and the signal handler will not work.
+ MarkPossibleNullPointerException(0);
- // We need to preserve EDI, but have no spare registers, so push it on the stack.
- // We have to remember that all stack addresses after this are offset by sizeof(EDI).
- NewLIR1(kX86Push32R, rs_rDI.GetReg());
+ // EDI is callee-save register in 32-bit mode.
+ NewLIR1(kX86Push32R, rs_rDI.GetReg());
+ }
- LIR *length_compare = nullptr;
- int start_value = 0;
- bool is_index_on_stack = false;
if (zero_based) {
+ // Start index is not present.
// We have to handle an empty string. Use special instruction JECXZ.
length_compare = NewLIR0(kX86Jecxz8);
+
+ // Copy the number of words to search in a temporary register.
+ // We will use the register at the end to calculate result.
+ OpRegReg(kOpMov, rs_tmp, rs_rCX);
} else {
+ // Start index is present.
rl_start = info->args[2];
+
// We have to offset by the start index.
if (rl_start.is_const) {
start_value = mir_graph_->ConstantValue(rl_start.orig_sreg);
@@ -1310,73 +1329,55 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
// Is the start > count?
length_compare = OpCmpImmBranch(kCondLe, rs_rCX, start_value, nullptr);
+ OpRegImm(kOpMov, rs_rDI, start_value);
+
+ // Copy the number of words to search in a temporary register.
+ // We will use the register at the end to calculate result.
+ OpRegReg(kOpMov, rs_tmp, rs_rCX);
if (start_value != 0) {
+ // Decrease the number of words to search by the start index.
OpRegImm(kOpSub, rs_rCX, start_value);
}
} else {
- // Runtime start index.
- rl_start = UpdateLocTyped(rl_start, kCoreReg);
- if (rl_start.location == kLocPhysReg) {
- // Handle "start index < 0" case.
- OpRegReg(kOpXor, tmpReg, tmpReg);
- OpRegReg(kOpCmp, rl_start.reg, tmpReg);
- OpCondRegReg(kOpCmov, kCondLt, rl_start.reg, tmpReg);
-
- // The length of the string should be greater than the start index.
- length_compare = OpCmpBranch(kCondLe, rs_rCX, rl_start.reg, nullptr);
- OpRegReg(kOpSub, rs_rCX, rl_start.reg);
- if (rl_start.reg == rs_rDI) {
- // The special case. We will use EDI further, so lets put start index to stack.
- NewLIR1(kX86Push32R, rs_rDI.GetReg());
- is_index_on_stack = true;
- }
- } else {
+ // Handle "start index < 0" case.
+ if (!cu_->target64 && rl_start.location != kLocPhysReg) {
// Load the start index from stack, remembering that we pushed EDI.
- int displacement = SRegOffset(rl_start.s_reg_low) +
- (cu_->target64 ? 2 : 1) * sizeof(uint32_t);
+ int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t);
{
ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
- Load32Disp(rs_rX86_SP, displacement, tmpReg);
+ Load32Disp(rs_rX86_SP, displacement, rs_rDI);
}
- OpRegReg(kOpXor, rs_rDI, rs_rDI);
- OpRegReg(kOpCmp, tmpReg, rs_rDI);
- OpCondRegReg(kOpCmov, kCondLt, tmpReg, rs_rDI);
-
- length_compare = OpCmpBranch(kCondLe, rs_rCX, tmpReg, nullptr);
- OpRegReg(kOpSub, rs_rCX, tmpReg);
- // Put the start index to stack.
- NewLIR1(kX86Push32R, tmpReg.GetReg());
- is_index_on_stack = true;
+ } else {
+ LoadValueDirectFixed(rl_start, rs_rDI);
}
+ OpRegReg(kOpXor, rs_tmp, rs_tmp);
+ OpRegReg(kOpCmp, rs_rDI, rs_tmp);
+ OpCondRegReg(kOpCmov, kCondLt, rs_rDI, rs_tmp);
+
+ // The length of the string should be greater than the start index.
+ length_compare = OpCmpBranch(kCondLe, rs_rCX, rs_rDI, nullptr);
+
+ // Copy the number of words to search in a temporary register.
+ // We will use the register at the end to calculate result.
+ OpRegReg(kOpMov, rs_tmp, rs_rCX);
+
+ // Decrease the number of words to search by the start index.
+ OpRegReg(kOpSub, rs_rCX, rs_rDI);
}
}
- DCHECK(length_compare != nullptr);
- // ECX now contains the count in words to be searched.
-
- // Load the address of the string into R11 or EBX (depending on mode).
+ // Load the address of the string into EDI.
+ // In case of start index we have to add the address to existing value in EDI.
// The string starts at VALUE(String) + 2 * OFFSET(String) + DATA_OFFSET.
- Load32Disp(rs_rDX, value_offset, rs_rDI);
- Load32Disp(rs_rDX, offset_offset, tmpReg);
- OpLea(tmpReg, rs_rDI, tmpReg, 1, data_offset);
-
- // Now compute into EDI where the search will start.
- if (zero_based || rl_start.is_const) {
- if (start_value == 0) {
- OpRegCopy(rs_rDI, tmpReg);
- } else {
- NewLIR3(kX86Lea32RM, rs_rDI.GetReg(), tmpReg.GetReg(), 2 * start_value);
- }
+ if (zero_based || (!zero_based && rl_start.is_const && start_value == 0)) {
+ Load32Disp(rs_rDX, offset_offset, rs_rDI);
} else {
- if (is_index_on_stack == true) {
- // Load the start index from stack.
- NewLIR1(kX86Pop32R, rs_rDX.GetReg());
- OpLea(rs_rDI, tmpReg, rs_rDX, 1, 0);
- } else {
- OpLea(rs_rDI, tmpReg, rl_start.reg, 1, 0);
- }
+ OpRegMem(kOpAdd, rs_rDI, rs_rDX, offset_offset);
}
+ OpRegImm(kOpLsl, rs_rDI, 1);
+ OpRegMem(kOpAdd, rs_rDI, rs_rDX, value_offset);
+ OpRegImm(kOpAdd, rs_rDI, data_offset);
// EDI now contains the start of the string to be searched.
// We are all prepared to do the search for the character.
@@ -1386,10 +1387,9 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
LIR* failed_branch = OpCondBranch(kCondNe, nullptr);
// yes, we matched. Compute the index of the result.
- // index = ((curr_ptr - orig_ptr) / 2) - 1.
- OpRegReg(kOpSub, rs_rDI, tmpReg);
- OpRegImm(kOpAsr, rs_rDI, 1);
- NewLIR3(kX86Lea32RM, rl_return.reg.GetReg(), rs_rDI.GetReg(), -1);
+ OpRegReg(kOpSub, rs_tmp, rs_rCX);
+ NewLIR3(kX86Lea32RM, rl_return.reg.GetReg(), rs_tmp.GetReg(), -1);
+
LIR *all_done = NewLIR1(kX86Jmp8, 0);
// Failed to match; return -1.
@@ -1400,8 +1400,9 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
// And join up at the end.
all_done->target = NewLIR0(kPseudoTargetLabel);
- // Restore EDI from the stack.
- NewLIR1(kX86Pop32R, rs_rDI.GetReg());
+
+ if (!cu_->target64)
+ NewLIR1(kX86Pop32R, rs_rDI.GetReg());
// Out of line code returns here.
if (slowpath_branch != nullptr) {
@@ -1410,6 +1411,15 @@ bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) {
}
StoreValue(rl_dest, rl_return);
+
+ FreeTemp(rs_rAX);
+ FreeTemp(rs_rCX);
+ FreeTemp(rs_rDX);
+ FreeTemp(rs_tmp);
+ if (cu_->target64) {
+ FreeTemp(rs_rDI);
+ }
+
return true;
}
@@ -2487,7 +2497,7 @@ int X86Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state,
in_to_reg_storage_mapping.Initialize(info->args, info->num_arg_words, &mapper);
const int last_mapped_in = in_to_reg_storage_mapping.GetMaxMappedIn();
const int size_of_the_last_mapped = last_mapped_in == -1 ? 1 :
- in_to_reg_storage_mapping.Get(last_mapped_in).Is64BitSolo() ? 2 : 1;
+ info->args[last_mapped_in].wide ? 2 : 1;
int regs_left_to_pass_via_stack = info->num_arg_words - (last_mapped_in + size_of_the_last_mapped);
// Fisrt of all, check whether it make sense to use bulk copying
diff --git a/compiler/dex/quick/x86/utility_x86.cc b/compiler/dex/quick/x86/utility_x86.cc
index 047a65d585..bae01d91bb 100644
--- a/compiler/dex/quick/x86/utility_x86.cc
+++ b/compiler/dex/quick/x86/utility_x86.cc
@@ -1050,6 +1050,7 @@ void X86Mir2Lir::AnalyzeInvokeStatic(int opcode, BasicBlock * bb, MIR *mir) {
->IsIntrinsic(index, &method)) {
switch (method.opcode) {
case kIntrinsicAbsDouble:
+ case kIntrinsicMinMaxDouble:
store_method_addr_ = true;
break;
default:
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index 17f9b916d4..500c6b84ea 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -534,10 +534,14 @@ enum X86OpCode {
Binary0fOpCode(kX86Ucomiss), // unordered float compare
Binary0fOpCode(kX86Comisd), // double compare
Binary0fOpCode(kX86Comiss), // float compare
- Binary0fOpCode(kX86Orps), // or of floating point registers
- Binary0fOpCode(kX86Xorps), // xor of floating point registers
- Binary0fOpCode(kX86Addsd), // double add
- Binary0fOpCode(kX86Addss), // float add
+ Binary0fOpCode(kX86Orpd), // double logical OR
+ Binary0fOpCode(kX86Orps), // float logical OR
+ Binary0fOpCode(kX86Andpd), // double logical AND
+ Binary0fOpCode(kX86Andps), // float logical AND
+ Binary0fOpCode(kX86Xorpd), // double logical XOR
+ Binary0fOpCode(kX86Xorps), // float logical XOR
+ Binary0fOpCode(kX86Addsd), // double ADD
+ Binary0fOpCode(kX86Addss), // float ADD
Binary0fOpCode(kX86Mulsd), // double multiply
Binary0fOpCode(kX86Mulss), // float multiply
Binary0fOpCode(kX86Cvtsd2ss), // double to float
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 7c9614f73a..cdf71b642e 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -41,6 +41,11 @@ class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
void ClassRejected(ClassReference ref) OVERRIDE;
+ // We are running in an environment where we can call patchoat safely so we should.
+ bool IsRelocationPossible() OVERRIDE {
+ return true;
+ }
+
private:
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index 892b30284f..4a3e071d1e 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -324,13 +324,15 @@ bool MIRGraph::InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed) {
}
for (int i = 0; ssa_rep->fp_use && i< ssa_rep->num_uses; i++) {
- if (ssa_rep->fp_use[i])
+ if (ssa_rep->fp_use[i]) {
changed |= SetFp(uses[i]);
}
+ }
for (int i = 0; ssa_rep->fp_def && i< ssa_rep->num_defs; i++) {
- if (ssa_rep->fp_def[i])
+ if (ssa_rep->fp_def[i]) {
changed |= SetFp(defs[i]);
}
+ }
// Special-case handling for moves & Phi
if (attrs & (DF_IS_MOVE | DF_NULL_TRANSFER_N)) {
/*
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 89295f2786..022ec6bc0d 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -231,8 +231,8 @@ inline int CompilerDriver::IsFastInvoke(
// the super class.
bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) &&
(referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) &&
- resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() &&
- (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method);
+ resolved_method->GetMethodIndex() < methods_class->GetVTableLength() &&
+ (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method);
if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) {
// Sharpen a virtual call into a direct call. The method_idx is into referrer's
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 9e88c8d875..f85bc65e91 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -955,7 +955,8 @@ bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_i
if (class_in_image) {
// boot -> app class pointers.
*is_type_initialized = resolved_class->IsInitialized();
- *use_direct_type_ptr = true;
+ // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+ *use_direct_type_ptr = !GetCompilerOptions().GetIncludePatchInformation();
*direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class);
return true;
} else {
@@ -1099,6 +1100,9 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
*direct_method = 0;
bool use_dex_cache = false;
const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
+ // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
+ const bool force_relocations = (compiling_boot ||
+ GetCompilerOptions().GetIncludePatchInformation());
if (compiler_->IsPortable()) {
if (sharp_type != kStatic && sharp_type != kDirect) {
return;
@@ -1109,7 +1113,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
return;
}
// TODO: support patching on all architectures.
- use_dex_cache = compiling_boot && !support_boot_image_fixup_;
+ use_dex_cache = force_relocations && !support_boot_image_fixup_;
}
bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
if (!use_dex_cache) {
@@ -1128,8 +1132,8 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
if (method_code_in_boot) {
*stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
}
- if (!use_dex_cache && compiling_boot) {
- if (!IsImageClass(method->GetDeclaringClassDescriptor())) {
+ if (!use_dex_cache && force_relocations) {
+ if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
// We can only branch directly to Methods that are resolved in the DexCache.
// Otherwise we won't invoke the resolution trampoline.
use_dex_cache = true;
@@ -1150,7 +1154,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
if (dex_method_idx != DexFile::kDexNoIndex) {
target_method->dex_method_index = dex_method_idx;
} else {
- if (compiling_boot && !use_dex_cache) {
+ if (force_relocations && !use_dex_cache) {
target_method->dex_method_index = method->GetDexMethodIndex();
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
}
@@ -1167,19 +1171,26 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType
*type = sharp_type;
}
} else {
- bool method_in_image = compiling_boot ||
+ bool method_in_image =
Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
- if (method_in_image) {
+ if (method_in_image || compiling_boot) {
+ // We know we must be able to get to the method in the image, so use that pointer.
CHECK(!method->IsAbstract());
*type = sharp_type;
- *direct_method = compiling_boot ? -1 : reinterpret_cast<uintptr_t>(method);
- *direct_code = compiling_boot ? -1 : compiler_->GetEntryPointOf(method);
+ *direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
+ *direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else if (!must_use_direct_pointers) {
// Set the code and rely on the dex cache for the method.
*type = sharp_type;
- *direct_code = compiler_->GetEntryPointOf(method);
+ if (force_relocations) {
+ *direct_code = -1;
+ target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ target_method->dex_method_index = method->GetDexMethodIndex();
+ } else {
+ *direct_code = compiler_->GetEntryPointOf(method);
+ }
} else {
// Direct pointers were required but none were available.
VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
new file mode 100644
index 0000000000..6112fbb5c0
--- /dev/null
+++ b/compiler/elf_patcher.cc
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "elf_patcher.h"
+
+#include <vector>
+#include <set>
+
+#include "elf_file.h"
+#include "elf_utils.h"
+#include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/string-inl.h"
+#include "oat.h"
+#include "os.h"
+#include "utils.h"
+
+namespace art {
+
+bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const std::string& oat_location,
+ ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+ if (oat_file == nullptr) {
+ CHECK(Runtime::Current()->IsCompiler());
+ oat_file = OatFile::Open(oat_location, oat_location, NULL, false, error_msg);
+ if (oat_file == nullptr) {
+ *error_msg = StringPrintf("Unable to find or open oat file at '%s': %s", oat_location.c_str(),
+ error_msg->c_str());
+ return false;
+ }
+ CHECK_EQ(class_linker->RegisterOatFile(oat_file), oat_file);
+ }
+ return ElfPatcher::Patch(driver, elf_file, oat_file,
+ reinterpret_cast<uintptr_t>(oat_file->Begin()), cb, cb_data, error_msg);
+}
+
+bool ElfPatcher::Patch(const CompilerDriver* driver, ElfFile* elf, const OatFile* oat_file,
+ uintptr_t oat_data_start, ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg) {
+ Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
+ if (data_sec == nullptr) {
+ *error_msg = "Unable to find .rodata section and oat header";
+ return false;
+ }
+ OatHeader* oat_header = reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
+ if (!oat_header->IsValid()) {
+ *error_msg = "Oat header was not valid";
+ return false;
+ }
+
+ ElfPatcher p(driver, elf, oat_file, oat_header, oat_data_start, cb, cb_data, error_msg);
+ return p.PatchElf();
+}
+
+mirror::ArtMethod* ElfPatcher::GetTargetMethod(const CompilerDriver::CallPatchInformation* patch) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::DexCache> dex_cache(
+ hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
+ mirror::ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
+ patch->GetTargetMethodIdx(),
+ dex_cache,
+ NullHandle<mirror::ClassLoader>(),
+ NullHandle<mirror::ArtMethod>(),
+ patch->GetTargetInvokeType());
+ CHECK(method != NULL)
+ << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
+ CHECK(!method->IsRuntimeMethod())
+ << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
+ CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
+ << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
+ << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
+ << PrettyMethod(method);
+ return method;
+}
+
+mirror::Class* ElfPatcher::GetTargetType(const CompilerDriver::TypePatchInformation* patch) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<2> hs(Thread::Current());
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
+ mirror::Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
+ dex_cache, NullHandle<mirror::ClassLoader>());
+ CHECK(klass != NULL)
+ << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
+ CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
+ << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
+ << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
+ << PrettyClass(klass);
+ return klass;
+}
+
+void ElfPatcher::AddPatch(uintptr_t p) {
+ if (write_patches_ && patches_set_.find(p) == patches_set_.end()) {
+ patches_set_.insert(p);
+ patches_.push_back(p);
+ }
+}
+
+uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) {
+ CHECK_GE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()));
+ uintptr_t off = patch_ptr - reinterpret_cast<uintptr_t>(oat_file_->Begin());
+ uintptr_t ret = reinterpret_cast<uintptr_t>(oat_header_) + off;
+
+ CHECK_GE(ret, reinterpret_cast<uintptr_t>(elf_file_->Begin()));
+ CHECK_LT(ret, reinterpret_cast<uintptr_t>(elf_file_->End()));
+ return reinterpret_cast<uint32_t*>(ret);
+}
+
+void ElfPatcher::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
+ patch->GetReferrerClassDefIdx(),
+ patch->GetReferrerMethodIdx());
+ // TODO: make this Thumb2 specific
+ uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
+ uintptr_t patch_ptr = reinterpret_cast<uintptr_t>(base + patch->GetLiteralOffset());
+ uint32_t* patch_location = GetPatchLocation(patch_ptr);
+ if (kIsDebugBuild) {
+ if (patch->IsCall()) {
+ const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
+ const DexFile::MethodId& id =
+ cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
+ uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
+ uint32_t actual = *patch_location;
+ CHECK(actual == expected || actual == value) << std::hex
+ << "actual=" << actual
+ << "expected=" << expected
+ << "value=" << value;
+ }
+ if (patch->IsType()) {
+ const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
+ const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
+ uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
+ uint32_t actual = *patch_location;
+ CHECK(actual == expected || actual == value) << std::hex
+ << "actual=" << actual
+ << "expected=" << expected
+ << "value=" << value;
+ }
+ }
+ *patch_location = value;
+ oat_header_->UpdateChecksum(patch_location, sizeof(value));
+
+ if (patch->IsCall() && patch->AsCall()->IsRelative()) {
+ // We never record relative patches.
+ return;
+ }
+ uintptr_t loc = patch_ptr - (reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
+ oat_header_->GetExecutableOffset());
+ CHECK_GT(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()) +
+ oat_header_->GetExecutableOffset());
+ CHECK_LT(loc, oat_file_->Size() - oat_header_->GetExecutableOffset());
+ AddPatch(loc);
+}
+
+bool ElfPatcher::PatchElf() {
+ // TODO if we are adding patches the resulting ELF file might have a
+ // potentially rather large amount of free space where patches might have been
+ // placed. We should adjust the ELF file to get rid of this excess space.
+ if (write_patches_) {
+ patches_.reserve(compiler_driver_->GetCodeToPatch().size() +
+ compiler_driver_->GetMethodsToPatch().size() +
+ compiler_driver_->GetClassesToPatch().size());
+ }
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const char* old_cause = self->StartAssertNoThreadSuspension("ElfPatcher");
+
+ typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
+ const CallPatches& code_to_patch = compiler_driver_->GetCodeToPatch();
+ for (size_t i = 0; i < code_to_patch.size(); i++) {
+ const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
+
+ mirror::ArtMethod* target = GetTargetMethod(patch);
+ uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
+ DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
+ const OatFile* target_oat = class_linker->FindOpenedOatFileForDexFile(*patch->GetTargetDexFile());
+ // Get where the data actually starts. if target is this oat_file_ it is oat_data_start_,
+ // otherwise it is wherever target_oat is loaded.
+ uintptr_t oat_data_addr = GetBaseAddressFor(target_oat);
+ uintptr_t code_base = reinterpret_cast<uintptr_t>(target_oat->Begin());
+ uintptr_t code_offset = quick_code - code_base;
+ bool is_quick_offset = false;
+ if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
+ is_quick_offset = true;
+ code_offset = oat_header_->GetQuickToInterpreterBridgeOffset();
+ } else if (quick_code ==
+ reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
+ CHECK(target->IsNative());
+ is_quick_offset = true;
+ code_offset = oat_header_->GetQuickGenericJniTrampolineOffset();
+ }
+ uintptr_t value;
+ if (patch->IsRelative()) {
+ // value to patch is relative to the location being patched
+ const void* quick_oat_code =
+ class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
+ patch->GetReferrerClassDefIdx(),
+ patch->GetReferrerMethodIdx());
+ if (is_quick_offset) {
+ // If its a quick offset it means that we are doing a relative patch from the class linker
+ // oat_file to the elf_patcher oat_file so we need to adjust the quick oat code to be the
+ // one in the output oat_file (ie where it is actually going to be loaded).
+ quick_code = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
+ quick_oat_code =
+ reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
+ oat_data_addr - code_base);
+ }
+ uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
+ uintptr_t patch_location = base + patch->GetLiteralOffset();
+ value = quick_code - patch_location + patch->RelativeOffset();
+ } else if (code_offset != 0) {
+ value = PointerToLowMemUInt32(reinterpret_cast<void*>(oat_data_addr + code_offset));
+ } else {
+ value = 0;
+ }
+ SetPatchLocation(patch, value);
+ }
+
+ const CallPatches& methods_to_patch = compiler_driver_->GetMethodsToPatch();
+ for (size_t i = 0; i < methods_to_patch.size(); i++) {
+ const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
+ mirror::ArtMethod* target = GetTargetMethod(patch);
+ SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+ }
+
+ const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
+ compiler_driver_->GetClassesToPatch();
+ for (size_t i = 0; i < classes_to_patch.size(); i++) {
+ const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
+ mirror::Class* target = GetTargetType(patch);
+ SetPatchLocation(patch, PointerToLowMemUInt32(get_image_address_(cb_data_, target)));
+ }
+
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ if (write_patches_) {
+ return WriteOutPatchData();
+ }
+ return true;
+}
+
+bool ElfPatcher::WriteOutPatchData() {
+ Elf32_Shdr* shdr = elf_file_->FindSectionByName(".oat_patches");
+ if (shdr != nullptr) {
+ CHECK_EQ(shdr, elf_file_->FindSectionByType(SHT_OAT_PATCH))
+ << "Incorrect type for .oat_patches section";
+ CHECK_LE(patches_.size() * sizeof(uintptr_t), shdr->sh_size)
+ << "We got more patches than anticipated";
+ CHECK_LE(reinterpret_cast<uintptr_t>(elf_file_->Begin()) + shdr->sh_offset + shdr->sh_size,
+ reinterpret_cast<uintptr_t>(elf_file_->End())) << "section is too large";
+ CHECK(shdr == &elf_file_->GetSectionHeader(elf_file_->GetSectionHeaderNum() - 1) ||
+ shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
+ << "Section overlaps onto next section";
+ // It's mmap'd so we can just memcpy.
+ memcpy(elf_file_->Begin() + shdr->sh_offset, patches_.data(),
+ patches_.size() * sizeof(uintptr_t));
+ // TODO We should fill in the newly empty space between the last patch and
+ // the start of the next section by moving the following sections down if
+ // possible.
+ shdr->sh_size = patches_.size() * sizeof(uintptr_t);
+ return true;
+ } else {
+ LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
+ *error_msg_ = "Unable to find section to write patch information to in ";
+ *error_msg_ += elf_file_->GetFile().GetPath();
+ return false;
+ }
+}
+
+} // namespace art
diff --git a/compiler/elf_patcher.h b/compiler/elf_patcher.h
new file mode 100644
index 0000000000..0a9f0a013e
--- /dev/null
+++ b/compiler/elf_patcher.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_ELF_PATCHER_H_
+#define ART_COMPILER_ELF_PATCHER_H_
+
+#include "base/mutex.h"
+#include "driver/compiler_driver.h"
+#include "elf_file.h"
+#include "mirror/art_method.h"
+#include "mirror/class.h"
+#include "mirror/object.h"
+#include "oat_file.h"
+#include "oat.h"
+#include "os.h"
+
+namespace art {
+
+class ElfPatcher {
+ public:
+ typedef void* (*ImageAddressCallback)(void* data, mirror::Object* obj);
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const std::string& oat_location,
+ ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const OatFile* oat_file, uintptr_t oat_data_begin,
+ ImageAddressCallback cb, void* cb_data,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const std::string& oat_location,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return ElfPatcher::Patch(driver, elf_file, oat_location,
+ DefaultImageAddressCallback, nullptr, error_msg);
+ }
+
+ static bool Patch(const CompilerDriver* driver, ElfFile* elf_file,
+ const OatFile* oat_file, uintptr_t oat_data_begin,
+ std::string* error_msg)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return ElfPatcher::Patch(driver, elf_file, oat_file, oat_data_begin,
+ DefaultImageAddressCallback, nullptr, error_msg);
+ }
+
+ private:
+ ElfPatcher(const CompilerDriver* driver, ElfFile* elf_file, const OatFile* oat_file,
+ OatHeader* oat_header, uintptr_t oat_data_begin,
+ ImageAddressCallback cb, void* cb_data, std::string* error_msg)
+ : compiler_driver_(driver), elf_file_(elf_file), oat_file_(oat_file),
+ oat_header_(oat_header), oat_data_begin_(oat_data_begin), get_image_address_(cb),
+ cb_data_(cb_data), error_msg_(error_msg),
+ write_patches_(compiler_driver_->GetCompilerOptions().GetIncludePatchInformation()) {}
+ ~ElfPatcher() {}
+
+ static void* DefaultImageAddressCallback(void* data_unused, mirror::Object* obj) {
+ return static_cast<void*>(obj);
+ }
+
+ bool PatchElf()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ mirror::Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void AddPatch(uintptr_t off);
+
+ void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ // Takes the pointer into the oat_file_ and get the pointer in to the ElfFile.
+ uint32_t* GetPatchLocation(uintptr_t patch_ptr);
+
+ bool WriteOutPatchData();
+
+ uintptr_t GetBaseAddressFor(const OatFile* f) {
+ if (f == oat_file_) {
+ return oat_data_begin_;
+ } else {
+ return reinterpret_cast<uintptr_t>(f->Begin());
+ }
+ }
+
+ const CompilerDriver* compiler_driver_;
+
+ // The elf_file containing the oat_data we are patching up
+ ElfFile* elf_file_;
+
+ // The oat_file that is actually loaded.
+ const OatFile* oat_file_;
+
+ // The oat_header_ within the elf_file_
+ OatHeader* oat_header_;
+
+ // Where the elf_file will be loaded during normal runs.
+ uintptr_t oat_data_begin_;
+
+ // Callback to get image addresses.
+ ImageAddressCallback get_image_address_;
+ void* cb_data_;
+
+ std::string* error_msg_;
+ std::vector<uintptr_t> patches_;
+ std::set<uintptr_t> patches_set_;
+ bool write_patches_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElfPatcher);
+};
+
+} // namespace art
+#endif // ART_COMPILER_ELF_PATCHER_H_
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index fe4fcd4177..3005e56a9a 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -80,7 +80,7 @@ TEST_F(ImageTest, WriteRead) {
t.NewTiming("WriteElf");
ScopedObjectAccess soa(Thread::Current());
SafeMap<std::string, std::string> key_value_store;
- OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, compiler_driver_.get(), &timings,
+ OatWriter oat_writer(class_linker->GetBootClassPath(), 0, 0, 0, compiler_driver_.get(), &timings,
&key_value_store);
bool success = compiler_driver_->WriteElf(GetTestAndroidRoot(),
!kIsTargetBuild,
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 8ef2964270..d102bbcedc 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -29,6 +29,7 @@
#include "driver/compiler_driver.h"
#include "elf_file.h"
#include "elf_utils.h"
+#include "elf_patcher.h"
#include "elf_writer.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
@@ -84,7 +85,7 @@ bool ImageWriter::Write(const std::string& image_filename,
return false;
}
std::string error_msg;
- oat_file_ = OatFile::OpenWritable(oat_file.get(), oat_location, &error_msg);
+ oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
if (oat_file_ == nullptr) {
LOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
<< ": " << error_msg;
@@ -801,214 +802,35 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) {
}
}
-static ArtMethod* GetTargetMethod(const CompilerDriver::CallPatchInformation* patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(
- hs.NewHandle(class_linker->FindDexCache(*patch->GetTargetDexFile())));
- ArtMethod* method = class_linker->ResolveMethod(*patch->GetTargetDexFile(),
- patch->GetTargetMethodIdx(),
- dex_cache,
- NullHandle<mirror::ClassLoader>(),
- NullHandle<mirror::ArtMethod>(),
- patch->GetTargetInvokeType());
- CHECK(method != NULL)
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
- CHECK(!method->IsRuntimeMethod())
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetTargetMethodIdx();
- CHECK(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx()) == method)
- << patch->GetTargetDexFile()->GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
- << PrettyMethod(dex_cache->GetResolvedMethods()->Get(patch->GetTargetMethodIdx())) << " "
- << PrettyMethod(method);
- return method;
-}
-
-static Class* GetTargetType(const CompilerDriver::TypePatchInformation* patch)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(patch->GetDexFile())));
- Class* klass = class_linker->ResolveType(patch->GetDexFile(), patch->GetTargetTypeIdx(),
- dex_cache, NullHandle<mirror::ClassLoader>());
- CHECK(klass != NULL)
- << patch->GetDexFile().GetLocation() << " " << patch->GetTargetTypeIdx();
- CHECK(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx()) == klass)
- << patch->GetDexFile().GetLocation() << " " << patch->GetReferrerMethodIdx() << " "
- << PrettyClass(dex_cache->GetResolvedTypes()->Get(patch->GetTargetTypeIdx())) << " "
- << PrettyClass(klass);
- return klass;
+static OatHeader* GetOatHeaderFromElf(ElfFile* elf) {
+ Elf32_Shdr* data_sec = elf->FindSectionByName(".rodata");
+ if (data_sec == nullptr) {
+ return nullptr;
+ }
+ return reinterpret_cast<OatHeader*>(elf->Begin() + data_sec->sh_offset);
}
void ImageWriter::PatchOatCodeAndMethods(File* elf_file) {
- std::vector<uintptr_t> patches;
- std::set<uintptr_t> patches_set;
- auto maybe_push = [&patches, &patches_set] (uintptr_t p) {
- if (patches_set.find(p) == patches_set.end()) {
- patches.push_back(p);
- patches_set.insert(p);
- }
- };
- const bool add_patches = compiler_driver_.GetCompilerOptions().GetIncludePatchInformation();
- if (add_patches) {
- // TODO if we are adding patches the resulting ELF file might have a potentially rather large
- // amount of free space where patches might have been placed. We should adjust the ELF file to
- // get rid of this excess space.
- patches.reserve(compiler_driver_.GetCodeToPatch().size() +
- compiler_driver_.GetMethodsToPatch().size() +
- compiler_driver_.GetClassesToPatch().size());
- }
- uintptr_t loc = 0;
- Thread* self = Thread::Current();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
-
- typedef std::vector<const CompilerDriver::CallPatchInformation*> CallPatches;
- const CallPatches& code_to_patch = compiler_driver_.GetCodeToPatch();
- for (size_t i = 0; i < code_to_patch.size(); i++) {
- const CompilerDriver::CallPatchInformation* patch = code_to_patch[i];
- ArtMethod* target = GetTargetMethod(patch);
- uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
- DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
- uintptr_t code_base = reinterpret_cast<uintptr_t>(&oat_file_->GetOatHeader());
- uintptr_t code_offset = quick_code - code_base;
- bool is_quick_offset = false;
- if (quick_code == reinterpret_cast<uintptr_t>(GetQuickToInterpreterBridge())) {
- is_quick_offset = true;
- code_offset = quick_to_interpreter_bridge_offset_;
- } else if (quick_code ==
- reinterpret_cast<uintptr_t>(class_linker->GetQuickGenericJniTrampoline())) {
- CHECK(target->IsNative());
- is_quick_offset = true;
- code_offset = quick_generic_jni_trampoline_offset_;
- }
- uintptr_t value;
- if (patch->IsRelative()) {
- // value to patch is relative to the location being patched
- const void* quick_oat_code =
- class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
- patch->GetReferrerClassDefIdx(),
- patch->GetReferrerMethodIdx());
- if (is_quick_offset) {
- // If its a quick offset it means that we are doing a relative patch from the class linker
- // oat_file to the image writer oat_file so we need to adjust the quick oat code to be the
- // one in the image writer oat_file.
- quick_code = PointerToLowMemUInt32(GetOatAddress(code_offset));
- quick_oat_code =
- reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(quick_oat_code) +
- reinterpret_cast<uintptr_t>(oat_data_begin_) - code_base);
- }
- uintptr_t base = reinterpret_cast<uintptr_t>(quick_oat_code);
- uintptr_t patch_location = base + patch->GetLiteralOffset();
- value = quick_code - patch_location + patch->RelativeOffset();
- } else {
- value = PointerToLowMemUInt32(GetOatAddress(code_offset));
- }
- SetPatchLocation(patch, value, &loc);
- if (add_patches && !patch->AsCall()->IsRelative()) {
- maybe_push(loc);
- }
- }
-
- const CallPatches& methods_to_patch = compiler_driver_.GetMethodsToPatch();
- for (size_t i = 0; i < methods_to_patch.size(); i++) {
- const CompilerDriver::CallPatchInformation* patch = methods_to_patch[i];
- ArtMethod* target = GetTargetMethod(patch);
- SetPatchLocation(patch, PointerToLowMemUInt32(GetImageAddress(target)), &loc);
- if (add_patches && !patch->AsCall()->IsRelative()) {
- maybe_push(loc);
- }
+ std::string error_msg;
+ std::unique_ptr<ElfFile> elf(ElfFile::Open(elf_file, PROT_READ|PROT_WRITE,
+ MAP_SHARED, &error_msg));
+ if (elf.get() == nullptr) {
+ LOG(FATAL) << "Unable patch oat file: " << error_msg;
+ return;
}
-
- const std::vector<const CompilerDriver::TypePatchInformation*>& classes_to_patch =
- compiler_driver_.GetClassesToPatch();
- for (size_t i = 0; i < classes_to_patch.size(); i++) {
- const CompilerDriver::TypePatchInformation* patch = classes_to_patch[i];
- Class* target = GetTargetType(patch);
- SetPatchLocation(patch, PointerToLowMemUInt32(GetImageAddress(target)), &loc);
- if (add_patches) {
- maybe_push(loc);
- }
+ if (!ElfPatcher::Patch(&compiler_driver_, elf.get(), oat_file_,
+ reinterpret_cast<uintptr_t>(oat_data_begin_),
+ GetImageAddressCallback, reinterpret_cast<void*>(this),
+ &error_msg)) {
+ LOG(FATAL) << "unable to patch oat file: " << error_msg;
+ return;
}
+ OatHeader* oat_header = GetOatHeaderFromElf(elf.get());
+ CHECK(oat_header != nullptr);
+ CHECK(oat_header->IsValid());
- // Update the image header with the new checksum after patching
ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
- image_header->SetOatChecksum(oat_file_->GetOatHeader().GetChecksum());
- self->EndAssertNoThreadSuspension(old_cause);
-
- // Update the ElfFiles SHT_OAT_PATCH section to include the patches.
- if (add_patches) {
- std::string err;
- // TODO we are mapping in the contents of this file twice. We should be able
- // to do it only once, which would be better.
- std::unique_ptr<ElfFile> file(ElfFile::Open(elf_file, true, false, &err));
- if (file == nullptr) {
- LOG(ERROR) << err;
- }
- Elf32_Shdr* shdr = file->FindSectionByName(".oat_patches");
- if (shdr != nullptr) {
- CHECK_EQ(shdr, file->FindSectionByType(SHT_OAT_PATCH))
- << "Incorrect type for .oat_patches section";
- CHECK_LE(patches.size() * sizeof(uintptr_t), shdr->sh_size)
- << "We got more patches than anticipated";
- CHECK_LE(reinterpret_cast<uintptr_t>(file->Begin()) + shdr->sh_offset + shdr->sh_size,
- reinterpret_cast<uintptr_t>(file->End())) << "section is too large";
- CHECK(shdr == &file->GetSectionHeader(file->GetSectionHeaderNum() - 1) ||
- shdr->sh_offset + shdr->sh_size <= (shdr + 1)->sh_offset)
- << "Section overlaps onto next section";
- // It's mmap'd so we can just memcpy.
- memcpy(file->Begin() + shdr->sh_offset, patches.data(), patches.size()*sizeof(uintptr_t));
- // TODO We should fill in the newly empty space between the last patch and the start of the
- // next section by moving the following sections down if possible.
- shdr->sh_size = patches.size() * sizeof(uintptr_t);
- } else {
- LOG(ERROR) << "Unable to find section header for SHT_OAT_PATCH";
- }
- }
-}
-
-void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value,
- uintptr_t* patched_ptr) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const void* quick_oat_code = class_linker->GetQuickOatCodeFor(patch->GetDexFile(),
- patch->GetReferrerClassDefIdx(),
- patch->GetReferrerMethodIdx());
- OatHeader& oat_header = const_cast<OatHeader&>(oat_file_->GetOatHeader());
- // TODO: make this Thumb2 specific
- uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(quick_oat_code) & ~0x1);
- uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset());
- if (kIsDebugBuild) {
- if (patch->IsCall()) {
- const CompilerDriver::CallPatchInformation* cpatch = patch->AsCall();
- const DexFile::MethodId& id = cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
- uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
- uint32_t actual = *patch_location;
- CHECK(actual == expected || actual == value) << std::hex
- << "actual=" << actual
- << "expected=" << expected
- << "value=" << value;
- }
- if (patch->IsType()) {
- const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
- const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
- uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
- uint32_t actual = *patch_location;
- CHECK(actual == expected || actual == value) << std::hex
- << "actual=" << actual
- << "expected=" << expected
- << "value=" << value;
- }
- }
- *patch_location = value;
- oat_header.UpdateChecksum(patch_location, sizeof(value));
-
- uintptr_t loc = reinterpret_cast<uintptr_t>(patch_location) -
- (reinterpret_cast<uintptr_t>(oat_file_->Begin()) + oat_header.GetExecutableOffset());
- CHECK_GT(reinterpret_cast<uintptr_t>(patch_location),
- reinterpret_cast<uintptr_t>(oat_file_->Begin()) + oat_header.GetExecutableOffset());
- CHECK_LT(loc, oat_file_->Size() - oat_header.GetExecutableOffset());
-
- *patched_ptr = loc;
+ image_header->SetOatChecksum(oat_header->GetChecksum());
}
} // namespace art
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index cf5bc930d1..e8bcf7f885 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -70,6 +70,11 @@ class ImageWriter {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
size_t GetImageOffset(mirror::Object* object) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ static void* GetImageAddressCallback(void* writer, mirror::Object* obj)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return reinterpret_cast<ImageWriter*>(writer)->GetImageAddress(obj);
+ }
+
mirror::Object* GetImageAddress(mirror::Object* object) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (object == NULL) {
@@ -159,9 +164,6 @@ class ImageWriter {
// Patches references in OatFile to expect runtime addresses.
void PatchOatCodeAndMethods(File* elf_file)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetPatchLocation(const CompilerDriver::PatchInformation* patch, uint32_t value,
- uintptr_t* patched_location)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const CompilerDriver& compiler_driver_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 84f0b3c001..11d17288e4 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -120,6 +120,7 @@ TEST_F(OatTest, WriteRead) {
OatWriter oat_writer(class_linker->GetBootClassPath(),
42U,
4096U,
+ 0,
compiler_driver_.get(),
&timings,
&key_value_store);
@@ -183,7 +184,7 @@ TEST_F(OatTest, WriteRead) {
TEST_F(OatTest, OatHeaderSizeCheck) {
// If this test is failing and you have to update these constants,
// it is time to update OatHeader::kOatVersion
- EXPECT_EQ(80U, sizeof(OatHeader));
+ EXPECT_EQ(84U, sizeof(OatHeader));
EXPECT_EQ(8U, sizeof(OatMethodOffsets));
EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
EXPECT_EQ(79 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 63a3c8ccdc..22f36f4f9b 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -50,6 +50,7 @@ namespace art {
OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
+ int32_t image_patch_delta,
const CompilerDriver* compiler,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store)
@@ -57,6 +58,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
dex_files_(&dex_files),
image_file_location_oat_checksum_(image_file_location_oat_checksum),
image_file_location_oat_begin_(image_file_location_oat_begin),
+ image_patch_delta_(image_patch_delta),
key_value_store_(key_value_store),
oat_header_(NULL),
size_dex_file_alignment_(0),
@@ -126,6 +128,7 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
CHECK_EQ(compiler->IsImage(),
key_value_store_->find(OatHeader::kImageLocationKey) == key_value_store_->end());
+ CHECK_ALIGNED(image_patch_delta_, kPageSize);
}
OatWriter::~OatWriter() {
@@ -808,6 +811,7 @@ size_t OatWriter::InitOatCode(size_t offset) {
oat_header_->SetExecutableOffset(offset);
size_executable_offset_alignment_ = offset - old_offset;
if (compiler_driver_->IsImage()) {
+ CHECK_EQ(image_patch_delta_, 0);
InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
#define DO_TRAMPOLINE(field, fn_name) \
@@ -840,6 +844,7 @@ size_t OatWriter::InitOatCode(size_t offset) {
oat_header_->SetQuickImtConflictTrampolineOffset(0);
oat_header_->SetQuickResolutionTrampolineOffset(0);
oat_header_->SetQuickToInterpreterBridgeOffset(0);
+ oat_header_->SetImagePatchDelta(image_patch_delta_);
}
return offset;
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 3d34956651..945048ecb7 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -79,6 +79,7 @@ class OatWriter {
OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uintptr_t image_file_location_oat_begin,
+ int32_t image_patch_delta,
const CompilerDriver* compiler,
TimingLogger* timings,
SafeMap<std::string, std::string>* key_value_store);
@@ -253,6 +254,7 @@ class OatWriter {
// dependencies on the image.
uint32_t image_file_location_oat_checksum_;
uintptr_t image_file_location_oat_begin_;
+ int32_t image_patch_delta_;
// data to write
SafeMap<std::string, std::string>* key_value_store_;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index f5941291e7..1f0b3613e7 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -34,6 +34,37 @@
namespace art {
+/**
+ * Helper class to add HTemporary instructions. This class is used when
+ * converting a DEX instruction to multiple HInstruction, and where those
+ * instructions do not die at the following instruction, but instead spans
+ * multiple instructions.
+ */
+class Temporaries : public ValueObject {
+ public:
+ Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) {
+ graph_->UpdateNumberOfTemporaries(count_);
+ }
+
+ void Add(HInstruction* instruction) {
+ // We currently only support vreg size temps.
+ DCHECK(instruction->GetType() != Primitive::kPrimLong
+ && instruction->GetType() != Primitive::kPrimDouble);
+ HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++);
+ instruction->GetBlock()->AddInstruction(temp);
+ DCHECK(temp->GetPrevious() == instruction);
+ }
+
+ private:
+ HGraph* const graph_;
+
+ // The total number of temporaries that will be used.
+ const size_t count_;
+
+ // Current index in the temporary stack, updated by `Add`.
+ size_t index_;
+};
+
static bool IsTypeSupported(Primitive::Type type) {
return type != Primitive::kPrimFloat && type != Primitive::kPrimDouble;
}
@@ -308,9 +339,13 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
arena_, number_of_arguments, return_type, dex_offset, method_idx);
size_t start_index = 0;
+ Temporaries temps(graph_, is_instance_call ? 1 : 0);
if (is_instance_call) {
HInstruction* arg = LoadLocal(is_range ? register_index : args[0], Primitive::kPrimNot);
- invoke->SetArgumentAt(0, arg);
+ HNullCheck* null_check = new (arena_) HNullCheck(arg, dex_offset);
+ current_block_->AddInstruction(null_check);
+ temps.Add(null_check);
+ invoke->SetArgumentAt(0, null_check);
start_index = 1;
}
@@ -343,37 +378,6 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
return true;
}
-/**
- * Helper class to add HTemporary instructions. This class is used when
- * converting a DEX instruction to multiple HInstruction, and where those
- * instructions do not die at the following instruction, but instead spans
- * multiple instructions.
- */
-class Temporaries : public ValueObject {
- public:
- Temporaries(HGraph* graph, size_t count) : graph_(graph), count_(count), index_(0) {
- graph_->UpdateNumberOfTemporaries(count_);
- }
-
- void Add(HInstruction* instruction) {
- // We currently only support vreg size temps.
- DCHECK(instruction->GetType() != Primitive::kPrimLong
- && instruction->GetType() != Primitive::kPrimDouble);
- HInstruction* temp = new (graph_->GetArena()) HTemporary(index_++);
- instruction->GetBlock()->AddInstruction(temp);
- DCHECK(temp->GetPrevious() == instruction);
- }
-
- private:
- HGraph* const graph_;
-
- // The total number of temporaries that will be used.
- const size_t count_;
-
- // Current index in the temporary stack, updated by `Add`.
- size_t index_;
-};
-
bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction,
uint32_t dex_offset,
bool is_put) {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index e0db0f18be..bd8c27ec3e 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -30,13 +30,16 @@
namespace art {
-void CodeGenerator::CompileBaseline(CodeAllocator* allocator) {
+void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
DCHECK(GoesToNextBlock(GetGraph()->GetEntryBlock(), blocks.Get(1)));
block_labels_.SetSize(blocks.Size());
DCHECK_EQ(frame_size_, kUninitializedFrameSize);
+ if (!is_leaf) {
+ MarkNotLeaf();
+ }
ComputeFrameSize(GetGraph()->GetMaximumNumberOfOutVRegs()
+ GetGraph()->GetNumberOfLocalVRegs()
+ GetGraph()->GetNumberOfTemporaries()
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 18e3e5a056..b31c3a3e83 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -70,7 +70,7 @@ class CodeGenerator : public ArenaObject {
public:
// Compiles the graph to executable instructions. Returns whether the compilation
// succeeded.
- void CompileBaseline(CodeAllocator* allocator);
+ void CompileBaseline(CodeAllocator* allocator, bool is_leaf = false);
void CompileOptimized(CodeAllocator* allocator);
static CodeGenerator* Create(ArenaAllocator* allocator,
HGraph* graph,
@@ -131,6 +131,14 @@ class CodeGenerator : public ArenaObject {
void BuildNativeGCMap(
std::vector<uint8_t>* vector, const DexCompilationUnit& dex_compilation_unit) const;
+ bool IsLeafMethod() const {
+ return is_leaf_;
+ }
+
+ void MarkNotLeaf() {
+ is_leaf_ = false;
+ }
+
protected:
CodeGenerator(HGraph* graph, size_t number_of_registers)
: frame_size_(kUninitializedFrameSize),
@@ -138,7 +146,8 @@ class CodeGenerator : public ArenaObject {
block_labels_(graph->GetArena(), 0),
pc_infos_(graph->GetArena(), 32),
slow_paths_(graph->GetArena(), 8),
- blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)) {}
+ blocked_registers_(graph->GetArena()->AllocArray<bool>(number_of_registers)),
+ is_leaf_(true) {}
~CodeGenerator() {}
// Register allocation logic.
@@ -171,6 +180,8 @@ class CodeGenerator : public ArenaObject {
// Temporary data structure used when doing register allocation.
bool* const blocked_registers_;
+ bool is_leaf_;
+
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
};
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 73c2d48320..90ec6cfdbd 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -17,12 +17,14 @@
#include "code_generator_arm.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "gc/accounting/card_table.h"
#include "mirror/array.h"
#include "mirror/art_method.h"
#include "thread.h"
#include "utils/assembler.h"
#include "utils/arm/assembler_arm.h"
#include "utils/arm/managed_register_arm.h"
+#include "utils/stack_checks.h"
namespace art {
@@ -32,6 +34,11 @@ arm::ArmManagedRegister Location::AsArm() const {
namespace arm {
+static constexpr bool kExplicitStackOverflowCheck = false;
+
+static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
+static constexpr int kCurrentMethodStackOffset = 0;
+
#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
class NullCheckSlowPathARM : public SlowPathCode {
@@ -51,6 +58,20 @@ class NullCheckSlowPathARM : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM);
};
+class StackOverflowCheckSlowPathARM : public SlowPathCode {
+ public:
+ StackOverflowCheckSlowPathARM() {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ LoadFromOffset(kLoadWord, PC, TR,
+ QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowStackOverflow).Int32Value());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathARM);
+};
+
#undef __
#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
@@ -82,9 +103,6 @@ inline Condition ARMOppositeCondition(IfCondition cond) {
return EQ; // Unreachable.
}
-static constexpr int kNumberOfPushedRegistersAtEntry = 1 + 2; // LR, R6, R7
-static constexpr int kCurrentMethodStackOffset = 0;
-
void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << ArmManagedRegister::FromCoreRegister(Register(reg));
}
@@ -97,7 +115,8 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph)
: CodeGenerator(graph, kNumberOfRegIds),
location_builder_(graph, this),
instruction_visitor_(graph, this),
- move_resolver_(graph->GetArena(), this) {}
+ move_resolver_(graph->GetArena(), this),
+ assembler_(true) {}
size_t CodeGeneratorARM::FrameEntrySpillSize() const {
return kNumberOfPushedRegistersAtEntry * kArmWordSize;
@@ -205,6 +224,22 @@ InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGene
codegen_(codegen) {}
void CodeGeneratorARM::GenerateFrameEntry() {
+ bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kArm);
+ if (!skip_overflow_check) {
+ if (kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM();
+ AddSlowPath(slow_path);
+
+ __ LoadFromOffset(kLoadWord, IP, TR, Thread::StackEndOffset<kArmWordSize>().Int32Value());
+ __ cmp(SP, ShifterOperand(IP));
+ __ b(slow_path->GetEntryLabel(), CC);
+ } else {
+ __ AddConstant(IP, SP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+ __ ldr(IP, Address(IP, 0));
+ RecordPcInfo(0);
+ }
+ }
+
core_spill_mask_ |= (1 << LR | 1 << R6 | 1 << R7);
__ PushList(1 << LR | 1 << R6 | 1 << R7);
@@ -377,11 +412,17 @@ void CodeGeneratorARM::Move64(Location destination, Location source) {
}
void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstruction* move_for) {
+ LocationSummary* locations = instruction->GetLocations();
+ if (locations != nullptr && locations->Out().Equals(location)) {
+ return;
+ }
+
if (instruction->AsIntConstant() != nullptr) {
int32_t value = instruction->AsIntConstant()->GetValue();
if (location.IsRegister()) {
__ LoadImmediate(location.AsArm().AsCoreRegister(), value);
} else {
+ DCHECK(location.IsStackSlot());
__ LoadImmediate(IP, value);
__ str(IP, Address(SP, location.GetStackIndex()));
}
@@ -391,6 +432,7 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr
__ LoadImmediate(location.AsArm().AsRegisterPairLow(), Low32Bits(value));
__ LoadImmediate(location.AsArm().AsRegisterPairHigh(), High32Bits(value));
} else {
+ DCHECK(location.IsDoubleStackSlot());
__ LoadImmediate(IP, Low32Bits(value));
__ str(IP, Address(SP, location.GetStackIndex()));
__ LoadImmediate(IP, High32Bits(value));
@@ -424,11 +466,11 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr
case Primitive::kPrimShort:
case Primitive::kPrimNot:
case Primitive::kPrimInt:
- Move32(location, instruction->GetLocations()->Out());
+ Move32(location, locations->Out());
break;
case Primitive::kPrimLong:
- Move64(location, instruction->GetLocations()->Out());
+ Move64(location, locations->Out());
break;
default:
@@ -478,20 +520,33 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
HCondition* condition = cond->AsCondition();
if (condition->NeedsMaterialization()) {
// Condition has been materialized, compare the output to 0
- if (!if_instr->GetLocations()->InAt(0).IsRegister()) {
- LOG(FATAL) << "Materialized condition is not in an ARM register";
- }
+ DCHECK(if_instr->GetLocations()->InAt(0).IsRegister());
__ cmp(if_instr->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
ShifterOperand(0));
__ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()), EQ);
} else {
// Condition has not been materialized, use its inputs as the comparison and its
// condition as the branch condition.
- __ cmp(condition->GetLocations()->InAt(0).AsArm().AsCoreRegister(),
- ShifterOperand(condition->GetLocations()->InAt(1).AsArm().AsCoreRegister()));
+ LocationSummary* locations = condition->GetLocations();
+ if (locations->InAt(1).IsRegister()) {
+ __ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
+ ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+ } else {
+ DCHECK(locations->InAt(1).IsConstant());
+ int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ ShifterOperand operand;
+ if (ShifterOperand::CanHoldArm(value, &operand)) {
+ __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
+ } else {
+ Register temp = IP;
+ __ LoadImmediate(temp, value);
+ __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
+ }
+ }
__ b(codegen_->GetLabelOf(if_instr->IfTrueSuccessor()),
ARMCondition(condition->GetCondition()));
}
+
if (!codegen_->GoesToNextBlock(if_instr->GetBlock(), if_instr->IfFalseSuccessor())) {
__ b(codegen_->GetLabelOf(if_instr->IfFalseSuccessor()));
}
@@ -501,7 +556,7 @@ void InstructionCodeGeneratorARM::VisitIf(HIf* if_instr) {
void LocationsBuilderARM::VisitCondition(HCondition* comp) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(comp->InputAt(1)));
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
@@ -509,16 +564,29 @@ void LocationsBuilderARM::VisitCondition(HCondition* comp) {
}
void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
- if (comp->NeedsMaterialization()) {
- LocationSummary* locations = comp->GetLocations();
+ if (!comp->NeedsMaterialization()) return;
+
+ LocationSummary* locations = comp->GetLocations();
+ if (locations->InAt(1).IsRegister()) {
__ cmp(locations->InAt(0).AsArm().AsCoreRegister(),
ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
- __ it(ARMCondition(comp->GetCondition()), kItElse);
- __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1),
- ARMCondition(comp->GetCondition()));
- __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0),
- ARMOppositeCondition(comp->GetCondition()));
+ } else {
+ DCHECK(locations->InAt(1).IsConstant());
+ int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ ShifterOperand operand;
+ if (ShifterOperand::CanHoldArm(value, &operand)) {
+ __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(value));
+ } else {
+ Register temp = IP;
+ __ LoadImmediate(temp, value);
+ __ cmp(locations->InAt(0).AsArm().AsCoreRegister(), ShifterOperand(temp));
+ }
}
+ __ it(ARMCondition(comp->GetCondition()), kItElse);
+ __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(1),
+ ARMCondition(comp->GetCondition()));
+ __ mov(locations->Out().AsArm().AsCoreRegister(), ShifterOperand(0),
+ ARMOppositeCondition(comp->GetCondition()));
}
void LocationsBuilderARM::VisitEqual(HEqual* comp) {
@@ -611,20 +679,17 @@ void InstructionCodeGeneratorARM::VisitStoreLocal(HStoreLocal* store) {
}
void LocationsBuilderARM::VisitIntConstant(HIntConstant* constant) {
- // TODO: Support constant locations.
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::ConstantLocation(constant));
constant->SetLocations(locations);
}
void InstructionCodeGeneratorARM::VisitIntConstant(HIntConstant* constant) {
- codegen_->Move(constant, constant->GetLocations()->Out(), nullptr);
}
void LocationsBuilderARM::VisitLongConstant(HLongConstant* constant) {
- // TODO: Support constant locations.
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::ConstantLocation(constant));
constant->SetLocations(locations);
}
@@ -688,6 +753,7 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) {
}
void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
locations->AddTemp(ArmCoreLocation(R0));
@@ -753,6 +819,7 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
__ blx(LR);
codegen_->RecordPcInfo(invoke->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitAdd(HAdd* add) {
@@ -761,7 +828,7 @@ void LocationsBuilderARM::VisitAdd(HAdd* add) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
break;
}
@@ -783,9 +850,15 @@ void InstructionCodeGeneratorARM::VisitAdd(HAdd* add) {
LocationSummary* locations = add->GetLocations();
switch (add->GetResultType()) {
case Primitive::kPrimInt:
- __ add(locations->Out().AsArm().AsCoreRegister(),
- locations->InAt(0).AsArm().AsCoreRegister(),
- ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+ if (locations->InAt(1).IsRegister()) {
+ __ add(locations->Out().AsArm().AsCoreRegister(),
+ locations->InAt(0).AsArm().AsCoreRegister(),
+ ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+ } else {
+ __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
+ locations->InAt(0).AsArm().AsCoreRegister(),
+ locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
+ }
break;
case Primitive::kPrimLong:
@@ -815,7 +888,7 @@ void LocationsBuilderARM::VisitSub(HSub* sub) {
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
locations->SetOut(Location::RequiresRegister());
break;
}
@@ -836,11 +909,18 @@ void LocationsBuilderARM::VisitSub(HSub* sub) {
void InstructionCodeGeneratorARM::VisitSub(HSub* sub) {
LocationSummary* locations = sub->GetLocations();
switch (sub->GetResultType()) {
- case Primitive::kPrimInt:
- __ sub(locations->Out().AsArm().AsCoreRegister(),
- locations->InAt(0).AsArm().AsCoreRegister(),
- ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+ case Primitive::kPrimInt: {
+ if (locations->InAt(1).IsRegister()) {
+ __ sub(locations->Out().AsArm().AsCoreRegister(),
+ locations->InAt(0).AsArm().AsCoreRegister(),
+ ShifterOperand(locations->InAt(1).AsArm().AsCoreRegister()));
+ } else {
+ __ AddConstant(locations->Out().AsArm().AsCoreRegister(),
+ locations->InAt(0).AsArm().AsCoreRegister(),
+ -locations->InAt(1).GetConstant()->AsIntConstant()->GetValue());
+ }
break;
+ }
case Primitive::kPrimLong:
__ subs(locations->Out().AsArm().AsRegisterPairLow(),
@@ -878,6 +958,7 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register> {
};
void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
InvokeRuntimeCallingConvention calling_convention;
locations->AddTemp(ArmCoreLocation(calling_convention.GetRegisterAt(0)));
@@ -896,6 +977,7 @@ void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
__ blx(LR);
codegen_->RecordPcInfo(instruction->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
@@ -948,9 +1030,11 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
ShifterOperand(right.AsRegisterPairHigh())); // Signed compare.
__ b(&less, LT);
__ b(&greater, GT);
+ // Do LoadImmediate before any `cmp`, as LoadImmediate might affect
+ // the status flags.
+ __ LoadImmediate(output, 0);
__ cmp(left.AsRegisterPairLow(),
ShifterOperand(right.AsRegisterPairLow())); // Unsigned compare.
- __ LoadImmediate(output, 0);
__ b(&done, EQ);
__ b(&less, CC);
@@ -986,6 +1070,11 @@ void LocationsBuilderARM::VisitInstanceFieldSet(HInstanceFieldSet* instruction)
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
+ // Temporary registers for the write barrier.
+ if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
instruction->SetLocations(locations);
}
@@ -1010,10 +1099,24 @@ void InstructionCodeGeneratorARM::VisitInstanceFieldSet(HInstanceFieldSet* instr
break;
}
- case Primitive::kPrimInt:
+ case Primitive::kPrimInt: {
+ Register value = locations->InAt(1).AsArm().AsCoreRegister();
+ __ StoreToOffset(kStoreWord, value, obj, offset);
+ break;
+ }
+
case Primitive::kPrimNot: {
Register value = locations->InAt(1).AsArm().AsCoreRegister();
__ StoreToOffset(kStoreWord, value, obj, offset);
+
+ Register temp = locations->GetTemp(0).AsArm().AsCoreRegister();
+ Register card = locations->GetTemp(1).AsArm().AsCoreRegister();
+ Label is_null;
+ __ CompareAndBranchIfZero(value, &is_null);
+ __ LoadFromOffset(kLoadWord, card, TR, Thread::CardTableOffset<kArmWordSize>().Int32Value());
+ __ Lsr(temp, obj, gc::accounting::CardTable::kCardShift);
+ __ strb(card, Address(card, temp));
+ __ Bind(&is_null);
break;
}
@@ -1158,7 +1261,16 @@ void ParallelMoveResolverARM::EmitMove(size_t index) {
__ StoreToOffset(kStoreWord, IP, SP, destination.GetStackIndex());
}
} else {
- LOG(FATAL) << "Unimplemented";
+ DCHECK(source.IsConstant());
+ DCHECK(source.GetConstant()->AsIntConstant() != nullptr);
+ int32_t value = source.GetConstant()->AsIntConstant()->GetValue();
+ if (destination.IsRegister()) {
+ __ LoadImmediate(destination.AsArm().AsCoreRegister(), value);
+ } else {
+ DCHECK(destination.IsStackSlot());
+ __ LoadImmediate(IP, value);
+ __ str(IP, Address(SP, destination.GetStackIndex()));
+ }
}
}
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 1b5974f9a2..85ab22b178 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -20,7 +20,7 @@
#include "code_generator.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
-#include "utils/arm/assembler_arm32.h"
+#include "utils/arm/assembler_thumb2.h"
namespace art {
namespace arm {
@@ -89,7 +89,7 @@ class LocationsBuilderARM : public HGraphVisitor {
#define DECLARE_VISIT_INSTRUCTION(name) \
virtual void Visit##name(H##name* instr);
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -107,7 +107,7 @@ class InstructionCodeGeneratorARM : public HGraphVisitor {
#define DECLARE_VISIT_INSTRUCTION(name) \
virtual void Visit##name(H##name* instr);
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -172,7 +172,7 @@ class CodeGeneratorARM : public CodeGenerator {
}
virtual InstructionSet GetInstructionSet() const OVERRIDE {
- return InstructionSet::kArm;
+ return InstructionSet::kThumb2;
}
private:
@@ -184,7 +184,7 @@ class CodeGeneratorARM : public CodeGenerator {
LocationsBuilderARM location_builder_;
InstructionCodeGeneratorARM instruction_visitor_;
ParallelMoveResolverARM move_resolver_;
- Arm32Assembler assembler_;
+ Thumb2Assembler assembler_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorARM);
};
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4e69a0cad8..251a2adf61 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_x86.h"
+#include "gc/accounting/card_table.h"
#include "utils/assembler.h"
+#include "utils/stack_checks.h"
#include "utils/x86/assembler_x86.h"
#include "utils/x86/managed_register_x86.h"
@@ -32,6 +34,11 @@ x86::X86ManagedRegister Location::AsX86() const {
namespace x86 {
+static constexpr bool kExplicitStackOverflowCheck = false;
+
+static constexpr int kNumberOfPushedRegistersAtEntry = 1;
+static constexpr int kCurrentMethodStackOffset = 0;
+
#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86 : public SlowPathCode {
@@ -49,6 +56,21 @@ class NullCheckSlowPathX86 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86);
};
+class StackOverflowCheckSlowPathX86 : public SlowPathCode {
+ public:
+ StackOverflowCheckSlowPathX86() {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ addl(ESP,
+ Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
+ __ fs()->jmp(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pThrowStackOverflow)));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86);
+};
+
#undef __
#define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
@@ -66,9 +88,6 @@ inline Condition X86Condition(IfCondition cond) {
return kEqual;
}
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
-static constexpr int kCurrentMethodStackOffset = 0;
-
void CodeGeneratorX86::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << X86ManagedRegister::FromCpuRegister(Register(reg));
}
@@ -183,8 +202,23 @@ void CodeGeneratorX86::GenerateFrameEntry() {
static const int kFakeReturnRegister = 8;
core_spill_mask_ |= (1 << kFakeReturnRegister);
+ bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86);
+ if (!skip_overflow_check && !kExplicitStackOverflowCheck) {
+ __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86))));
+ RecordPcInfo(0);
+ }
+
// The return PC has already been pushed on the stack.
__ subl(ESP, Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86WordSize));
+
+ if (!skip_overflow_check && kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86();
+ AddSlowPath(slow_path);
+
+ __ fs()->cmpl(ESP, Address::Absolute(Thread::StackEndOffset<kX86WordSize>()));
+ __ j(kLess, slow_path->GetEntryLabel());
+ }
+
__ movl(Address(ESP, kCurrentMethodStackOffset), EAX);
}
@@ -473,6 +507,10 @@ void InstructionCodeGeneratorX86::VisitIf(HIf* if_instr) {
// LHS is guaranteed to be in a register (see LocationsBuilderX86::VisitCondition).
if (rhs.IsRegister()) {
__ cmpl(lhs.AsX86().AsCpuRegister(), rhs.AsX86().AsCpuRegister());
+ } else if (rhs.IsConstant()) {
+ HIntConstant* instruction = rhs.GetConstant()->AsIntConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ __ cmpl(lhs.AsX86().AsCpuRegister(), imm);
} else {
__ cmpl(lhs.AsX86().AsCpuRegister(), Address(ESP, rhs.GetStackIndex()));
}
@@ -530,7 +568,7 @@ void LocationsBuilderX86::VisitCondition(HCondition* comp) {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
if (comp->NeedsMaterialization()) {
- locations->SetOut(Location::SameAsFirstInput());
+ locations->SetOut(Location::RequiresRegister());
}
comp->SetLocations(locations);
}
@@ -541,6 +579,10 @@ void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
if (locations->InAt(1).IsRegister()) {
__ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
locations->InAt(1).AsX86().AsCpuRegister());
+ } else if (locations->InAt(1).IsConstant()) {
+ HConstant* instruction = locations->InAt(1).GetConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ __ cmpl(locations->InAt(0).AsX86().AsCpuRegister(), imm);
} else {
__ cmpl(locations->InAt(0).AsX86().AsCpuRegister(),
Address(ESP, locations->InAt(1).GetStackIndex()));
@@ -598,20 +640,17 @@ void InstructionCodeGeneratorX86::VisitGreaterThanOrEqual(HGreaterThanOrEqual* c
}
void LocationsBuilderX86::VisitIntConstant(HIntConstant* constant) {
- // TODO: Support constant locations.
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::ConstantLocation(constant));
constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86::VisitIntConstant(HIntConstant* constant) {
- codegen_->Move(constant, constant->GetLocations()->Out(), nullptr);
}
void LocationsBuilderX86::VisitLongConstant(HLongConstant* constant) {
- // TODO: Support constant locations.
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::ConstantLocation(constant));
constant->SetLocations(locations);
}
@@ -676,6 +715,7 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) {
}
void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
locations->AddTemp(X86CpuLocation(EAX));
@@ -733,6 +773,7 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
// (temp + offset_of_quick_compiled_code)()
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
+ DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke->GetDexPc());
}
@@ -769,6 +810,10 @@ void InstructionCodeGeneratorX86::VisitAdd(HAdd* add) {
if (locations->InAt(1).IsRegister()) {
__ addl(locations->InAt(0).AsX86().AsCpuRegister(),
locations->InAt(1).AsX86().AsCpuRegister());
+ } else if (locations->InAt(1).IsConstant()) {
+ HConstant* instruction = locations->InAt(1).GetConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ __ addl(locations->InAt(0).AsX86().AsCpuRegister(), imm);
} else {
__ addl(locations->InAt(0).AsX86().AsCpuRegister(),
Address(ESP, locations->InAt(1).GetStackIndex()));
@@ -838,6 +883,10 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
if (locations->InAt(1).IsRegister()) {
__ subl(locations->InAt(0).AsX86().AsCpuRegister(),
locations->InAt(1).AsX86().AsCpuRegister());
+ } else if (locations->InAt(1).IsConstant()) {
+ HConstant* instruction = locations->InAt(1).GetConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ __ subl(locations->InAt(0).AsX86().AsCpuRegister(), imm);
} else {
__ subl(locations->InAt(0).AsX86().AsCpuRegister(),
Address(ESP, locations->InAt(1).GetStackIndex()));
@@ -875,6 +924,7 @@ void InstructionCodeGeneratorX86::VisitSub(HSub* sub) {
}
void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetOut(X86CpuLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
@@ -892,6 +942,7 @@ void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pAllocObjectWithAccessCheck)));
codegen_->RecordPcInfo(instruction->GetDexPc());
+ DCHECK(!codegen_->IsLeafMethod());
}
void LocationsBuilderX86::VisitParameterValue(HParameterValue* instruction) {
@@ -996,6 +1047,12 @@ void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction)
} else {
locations->SetInAt(1, Location::RequiresRegister());
}
+ // Temporary registers for the write barrier.
+ if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ locations->AddTemp(Location::RequiresRegister());
+ // Ensure the card is in a byte register.
+ locations->AddTemp(X86CpuLocation(ECX));
+ }
instruction->SetLocations(locations);
}
@@ -1020,10 +1077,25 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr
break;
}
- case Primitive::kPrimInt:
+ case Primitive::kPrimInt: {
+ Register value = locations->InAt(1).AsX86().AsCpuRegister();
+ __ movl(Address(obj, offset), value);
+ break;
+ }
+
case Primitive::kPrimNot: {
Register value = locations->InAt(1).AsX86().AsCpuRegister();
__ movl(Address(obj, offset), value);
+ Label is_null;
+ Register temp = locations->GetTemp(0).AsX86().AsCpuRegister();
+ Register card = locations->GetTemp(1).AsX86().AsCpuRegister();
+ __ testl(value, value);
+ __ j(kEqual, &is_null);
+ __ fs()->movl(card, Address::Absolute(Thread::CardTableOffset<kX86WordSize>().Int32Value()));
+ __ movl(temp, obj);
+ __ shrl(temp, Immediate(gc::accounting::CardTable::kCardShift));
+ __ movb(Address(temp, card, TIMES_1, 0), locations->GetTemp(1).AsX86().AsByteRegister());
+ __ Bind(&is_null);
break;
}
@@ -1178,6 +1250,14 @@ void ParallelMoveResolverX86::EmitMove(size_t index) {
MoveMemoryToMemory(destination.GetStackIndex(),
source.GetStackIndex());
}
+ } else if (source.IsConstant()) {
+ HIntConstant* instruction = source.GetConstant()->AsIntConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ if (destination.IsRegister()) {
+ __ movl(destination.AsX86().AsCpuRegister(), imm);
+ } else {
+ __ movl(Address(ESP, destination.GetStackIndex()), imm);
+ }
} else {
LOG(FATAL) << "Unimplemented";
}
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index d622d2a685..b7c2ad8319 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -90,7 +90,7 @@ class LocationsBuilderX86 : public HGraphVisitor {
#define DECLARE_VISIT_INSTRUCTION(name) \
virtual void Visit##name(H##name* instr);
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -108,7 +108,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
#define DECLARE_VISIT_INSTRUCTION(name) \
virtual void Visit##name(H##name* instr);
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -126,7 +126,7 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
class CodeGeneratorX86 : public CodeGenerator {
public:
explicit CodeGeneratorX86(HGraph* graph);
- virtual ~CodeGeneratorX86() { }
+ virtual ~CodeGeneratorX86() {}
virtual void GenerateFrameEntry() OVERRIDE;
virtual void GenerateFrameExit() OVERRIDE;
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index e3ce5ceb4f..641e8e178b 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -17,11 +17,13 @@
#include "code_generator_x86_64.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "gc/accounting/card_table.h"
#include "mirror/array.h"
#include "mirror/art_method.h"
#include "mirror/object_reference.h"
#include "thread.h"
#include "utils/assembler.h"
+#include "utils/stack_checks.h"
#include "utils/x86_64/assembler_x86_64.h"
#include "utils/x86_64/managed_register_x86_64.h"
@@ -33,6 +35,15 @@ x86_64::X86_64ManagedRegister Location::AsX86_64() const {
namespace x86_64 {
+static constexpr bool kExplicitStackOverflowCheck = true;
+
+// Some x86_64 instructions require a register to be available as temp.
+static constexpr Register TMP = R11;
+
+static constexpr int kNumberOfPushedRegistersAtEntry = 1;
+static constexpr int kCurrentMethodStackOffset = 0;
+
+
#define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())->
class NullCheckSlowPathX86_64 : public SlowPathCode {
@@ -41,7 +52,8 @@ class NullCheckSlowPathX86_64 : public SlowPathCode {
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
__ Bind(GetEntryLabel());
- __ gs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
+ __ gs()->call(
+ Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowNullPointer), true));
codegen->RecordPcInfo(dex_pc_);
}
@@ -50,6 +62,22 @@ class NullCheckSlowPathX86_64 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathX86_64);
};
+class StackOverflowCheckSlowPathX86_64 : public SlowPathCode {
+ public:
+ StackOverflowCheckSlowPathX86_64() {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ __ Bind(GetEntryLabel());
+ __ addq(CpuRegister(RSP),
+ Immediate(codegen->GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+ __ gs()->jmp(
+ Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowStackOverflow), true));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackOverflowCheckSlowPathX86_64);
+};
+
#undef __
#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
@@ -67,12 +95,6 @@ inline Condition X86_64Condition(IfCondition cond) {
return kEqual;
}
-// Some x86_64 instructions require a register to be available as temp.
-static constexpr Register TMP = R11;
-
-static constexpr int kNumberOfPushedRegistersAtEntry = 1;
-static constexpr int kCurrentMethodStackOffset = 0;
-
void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << X86_64ManagedRegister::FromCpuRegister(Register(reg));
}
@@ -147,7 +169,26 @@ void CodeGeneratorX86_64::GenerateFrameEntry() {
core_spill_mask_ |= (1 << kFakeReturnRegister);
// The return PC has already been pushed on the stack.
- __ subq(CpuRegister(RSP), Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+ __ subq(CpuRegister(RSP),
+ Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize));
+
+ bool skip_overflow_check = IsLeafMethod()
+ && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86_64);
+
+ if (!skip_overflow_check) {
+ if (kExplicitStackOverflowCheck) {
+ SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathX86_64();
+ AddSlowPath(slow_path);
+
+ __ gs()->cmpq(CpuRegister(RSP),
+ Address::Absolute(Thread::StackEndOffset<kX86_64WordSize>(), true));
+ __ j(kLess, slow_path->GetEntryLabel());
+ } else {
+ __ testq(CpuRegister(RAX), Address(
+ CpuRegister(RSP), -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86_64))));
+ }
+ }
+
__ movl(Address(CpuRegister(RSP), kCurrentMethodStackOffset), CpuRegister(RDI));
}
@@ -329,7 +370,14 @@ void InstructionCodeGeneratorX86_64::VisitIf(HIf* if_instr) {
} else {
Location lhs = condition->GetLocations()->InAt(0);
Location rhs = condition->GetLocations()->InAt(1);
- __ cmpl(lhs.AsX86_64().AsCpuRegister(), rhs.AsX86_64().AsCpuRegister());
+ if (rhs.IsRegister()) {
+ __ cmpl(lhs.AsX86_64().AsCpuRegister(), rhs.AsX86_64().AsCpuRegister());
+ } else if (rhs.IsConstant()) {
+ __ cmpl(lhs.AsX86_64().AsCpuRegister(),
+ Immediate(rhs.GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ __ cmpl(lhs.AsX86_64().AsCpuRegister(), Address(CpuRegister(RSP), rhs.GetStackIndex()));
+ }
__ j(X86_64Condition(condition->GetCondition()),
codegen_->GetLabelOf(if_instr->IfTrueSuccessor()));
}
@@ -382,7 +430,7 @@ void InstructionCodeGeneratorX86_64::VisitStoreLocal(HStoreLocal* store) {
void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(comp);
locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
if (comp->NeedsMaterialization()) {
locations->SetOut(Location::RequiresRegister());
}
@@ -391,8 +439,17 @@ void LocationsBuilderX86_64::VisitCondition(HCondition* comp) {
void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
if (comp->NeedsMaterialization()) {
- __ cmpq(comp->GetLocations()->InAt(0).AsX86_64().AsCpuRegister(),
- comp->GetLocations()->InAt(1).AsX86_64().AsCpuRegister());
+ LocationSummary* locations = comp->GetLocations();
+ if (locations->InAt(1).IsRegister()) {
+ __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ locations->InAt(1).AsX86_64().AsCpuRegister());
+ } else if (locations->InAt(1).IsConstant()) {
+ __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ Immediate(locations->InAt(1).GetConstant()->AsIntConstant()->GetValue()));
+ } else {
+ __ cmpq(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+ }
__ setcc(X86_64Condition(comp->GetCondition()),
comp->GetLocations()->Out().AsX86_64().AsCpuRegister());
}
@@ -480,25 +537,21 @@ void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
}
void LocationsBuilderX86_64::VisitIntConstant(HIntConstant* constant) {
- // TODO: Support constant locations.
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::ConstantLocation(constant));
constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitIntConstant(HIntConstant* constant) {
- codegen_->Move(constant, constant->GetLocations()->Out(), nullptr);
}
void LocationsBuilderX86_64::VisitLongConstant(HLongConstant* constant) {
- // TODO: Support constant locations.
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(constant);
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::ConstantLocation(constant));
constant->SetLocations(locations);
}
void InstructionCodeGeneratorX86_64::VisitLongConstant(HLongConstant* constant) {
- codegen_->Move(constant, constant->GetLocations()->Out(), nullptr);
}
void LocationsBuilderX86_64::VisitReturnVoid(HReturnVoid* ret) {
@@ -606,6 +659,7 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type
}
void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke);
locations->AddTemp(X86_64CpuLocation(RDI));
@@ -660,13 +714,19 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
// (temp + offset_of_quick_compiled_code)()
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
+ DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(invoke->GetDexPc());
}
void LocationsBuilderX86_64::VisitAdd(HAdd* add) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(add);
switch (add->GetResultType()) {
- case Primitive::kPrimInt:
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ }
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
@@ -693,8 +753,17 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
locations->Out().AsX86_64().AsCpuRegister().AsRegister());
switch (add->GetResultType()) {
case Primitive::kPrimInt: {
- __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(),
- locations->InAt(1).AsX86_64().AsCpuRegister());
+ if (locations->InAt(1).IsRegister()) {
+ __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ locations->InAt(1).AsX86_64().AsCpuRegister());
+ } else if (locations->InAt(1).IsConstant()) {
+ HConstant* instruction = locations->InAt(1).GetConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(), imm);
+ } else {
+ __ addl(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+ }
break;
}
case Primitive::kPrimLong: {
@@ -718,7 +787,12 @@ void InstructionCodeGeneratorX86_64::VisitAdd(HAdd* add) {
void LocationsBuilderX86_64::VisitSub(HSub* sub) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(sub);
switch (sub->GetResultType()) {
- case Primitive::kPrimInt:
+ case Primitive::kPrimInt: {
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::Any());
+ locations->SetOut(Location::SameAsFirstInput());
+ break;
+ }
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
@@ -745,8 +819,17 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
locations->Out().AsX86_64().AsCpuRegister().AsRegister());
switch (sub->GetResultType()) {
case Primitive::kPrimInt: {
- __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(),
- locations->InAt(1).AsX86_64().AsCpuRegister());
+ if (locations->InAt(1).IsRegister()) {
+ __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ locations->InAt(1).AsX86_64().AsCpuRegister());
+ } else if (locations->InAt(1).IsConstant()) {
+ HConstant* instruction = locations->InAt(1).GetConstant();
+ Immediate imm(instruction->AsIntConstant()->GetValue());
+ __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(), imm);
+ } else {
+ __ subl(locations->InAt(0).AsX86_64().AsCpuRegister(),
+ Address(CpuRegister(RSP), locations->InAt(1).GetStackIndex()));
+ }
break;
}
case Primitive::kPrimLong: {
@@ -768,6 +851,7 @@ void InstructionCodeGeneratorX86_64::VisitSub(HSub* sub) {
}
void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
+ codegen_->MarkNotLeaf();
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetOut(X86_64CpuLocation(RAX));
instruction->SetLocations(locations);
@@ -781,6 +865,7 @@ void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction)
__ gs()->call(Address::Absolute(
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pAllocObjectWithAccessCheck), true));
+ DCHECK(!codegen_->IsLeafMethod());
codegen_->RecordPcInfo(instruction->GetDexPc());
}
@@ -831,6 +916,11 @@ void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instructio
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
+ // Temporary registers for the write barrier.
+ if (instruction->InputAt(1)->GetType() == Primitive::kPrimNot) {
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
instruction->SetLocations(locations);
}
@@ -854,9 +944,24 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in
break;
}
- case Primitive::kPrimInt:
+ case Primitive::kPrimInt: {
+ __ movl(Address(obj, offset), value);
+ break;
+ }
+
case Primitive::kPrimNot: {
__ movl(Address(obj, offset), value);
+ Label is_null;
+ CpuRegister temp = locations->GetTemp(0).AsX86_64().AsCpuRegister();
+ CpuRegister card = locations->GetTemp(1).AsX86_64().AsCpuRegister();
+ __ testl(value, value);
+ __ j(kEqual, &is_null);
+ __ gs()->movq(card, Address::Absolute(
+ Thread::CardTableOffset<kX86_64WordSize>().Int32Value(), true));
+ __ movq(temp, obj);
+ __ shrq(temp, Immediate(gc::accounting::CardTable::kCardShift));
+ __ movb(Address(temp, card, TIMES_1, 0), card);
+ __ Bind(&is_null);
break;
}
@@ -1008,6 +1113,26 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) {
__ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex()));
__ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
}
+ } else if (source.IsConstant()) {
+ HConstant* constant = source.GetConstant();
+ if (constant->IsIntConstant()) {
+ Immediate imm(constant->AsIntConstant()->GetValue());
+ if (destination.IsRegister()) {
+ __ movl(destination.AsX86_64().AsCpuRegister(), imm);
+ } else {
+ __ movl(Address(CpuRegister(RSP), destination.GetStackIndex()), imm);
+ }
+ } else if (constant->IsLongConstant()) {
+ int64_t value = constant->AsLongConstant()->GetValue();
+ if (destination.IsRegister()) {
+ __ movq(destination.AsX86_64().AsCpuRegister(), Immediate(value));
+ } else {
+ __ movq(CpuRegister(TMP), Immediate(value));
+ __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP));
+ }
+ } else {
+ LOG(FATAL) << "Unimplemented constant type";
+ }
} else {
LOG(FATAL) << "Unimplemented";
}
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 8283dda4a5..a20ca3f260 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -87,7 +87,7 @@ class LocationsBuilderX86_64 : public HGraphVisitor {
#define DECLARE_VISIT_INSTRUCTION(name) \
virtual void Visit##name(H##name* instr);
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -105,7 +105,7 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor {
#define DECLARE_VISIT_INSTRUCTION(name) \
virtual void Visit##name(H##name* instr);
- FOR_EACH_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index 7ec0c84167..d7ac10d164 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -48,10 +48,17 @@ class InternalCodeAllocator : public CodeAllocator {
};
#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
-static void Run(const InternalCodeAllocator& allocator, bool has_result, int32_t expected) {
+static void Run(const InternalCodeAllocator& allocator,
+ const CodeGenerator& codegen,
+ bool has_result,
+ int32_t expected) {
typedef int32_t (*fptr)();
CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize());
fptr f = reinterpret_cast<fptr>(allocator.GetMemory());
+ if (codegen.GetInstructionSet() == kThumb2) {
+ // For thumb we need the bottom bit set.
+ f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
+ }
int32_t result = f();
if (has_result) {
CHECK_EQ(result, expected);
@@ -69,21 +76,23 @@ static void TestCode(const uint16_t* data, bool has_result = false, int32_t expe
InternalCodeAllocator allocator;
CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, kX86);
- codegen->CompileBaseline(&allocator);
+ // We avoid doing a stack overflow check that requires the runtime being setup,
+ // by making sure the compiler knows the methods we are running are leaf methods.
+ codegen->CompileBaseline(&allocator, true);
#if defined(__i386__)
- Run(allocator, has_result, expected);
+ Run(allocator, *codegen, has_result, expected);
#endif
codegen = CodeGenerator::Create(&arena, graph, kArm);
- codegen->CompileBaseline(&allocator);
+ codegen->CompileBaseline(&allocator, true);
#if defined(__arm__)
- Run(allocator, has_result, expected);
+ Run(allocator, *codegen, has_result, expected);
#endif
codegen = CodeGenerator::Create(&arena, graph, kX86_64);
- codegen->CompileBaseline(&allocator);
+ codegen->CompileBaseline(&allocator, true);
#if defined(__x86_64__)
- Run(allocator, has_result, expected);
+ Run(allocator, *codegen, has_result, expected);
#endif
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f033e2e22b..f011e85cc0 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -108,6 +108,10 @@ class HGraphVisualizerPrinter : public HGraphVisitor {
} else {
codegen_.DumpCoreRegister(output_, location.reg().RegId());
}
+ } else if (location.IsConstant()) {
+ output_ << "constant";
+ } else if (location.IsInvalid()) {
+ output_ << "invalid";
} else if (location.IsStackSlot()) {
output_ << location.GetStackIndex() << "(sp)";
} else {
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 98766d2701..468cfb7923 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -29,4 +29,11 @@ LocationSummary::LocationSummary(HInstruction* instruction)
}
}
+
+Location Location::RegisterOrConstant(HInstruction* instruction) {
+ return instruction->IsConstant()
+ ? Location::ConstantLocation(instruction->AsConstant())
+ : Location::RequiresRegister();
+}
+
} // namespace art
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 40a39ad80d..aaddb099a6 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -24,6 +24,7 @@
namespace art {
+class HConstant;
class HInstruction;
/**
@@ -34,23 +35,33 @@ class Location : public ValueObject {
public:
enum Kind {
kInvalid = 0,
- kStackSlot = 1, // Word size slot.
- kDoubleStackSlot = 2, // 64bit stack slot.
- kRegister = 3,
+ kConstant = 1,
+ kStackSlot = 2, // Word size slot.
+ kDoubleStackSlot = 3, // 64bit stack slot.
+ kRegister = 4,
// On 32bits architectures, quick can pass a long where the
// low bits are in the last parameter register, and the high
// bits are in a stack slot. The kQuickParameter kind is for
// handling this special case.
- kQuickParameter = 4,
+ kQuickParameter = 5,
// Unallocated location represents a location that is not fixed and can be
// allocated by a register allocator. Each unallocated location has
// a policy that specifies what kind of location is suitable. Payload
// contains register allocation policy.
- kUnallocated = 5,
+ kUnallocated = 6,
};
Location() : value_(kInvalid) {
+ // Verify that non-tagged location kinds do not interfere with kConstantTag.
+ COMPILE_ASSERT((kInvalid & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kUnallocated & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kStackSlot & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kDoubleStackSlot & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kRegister & kLocationTagMask) != kConstant, TagError);
+ COMPILE_ASSERT((kConstant & kLocationTagMask) == kConstant, TagError);
+ COMPILE_ASSERT((kQuickParameter & kLocationTagMask) == kConstant, TagError);
+
DCHECK(!IsValid());
}
@@ -61,6 +72,20 @@ class Location : public ValueObject {
return *this;
}
+ bool IsConstant() const {
+ return (value_ & kLocationTagMask) == kConstant;
+ }
+
+ static Location ConstantLocation(HConstant* constant) {
+ DCHECK(constant != nullptr);
+ return Location(kConstant | reinterpret_cast<uword>(constant));
+ }
+
+ HConstant* GetConstant() const {
+ DCHECK(IsConstant());
+ return reinterpret_cast<HConstant*>(value_ & ~kLocationTagMask);
+ }
+
bool IsValid() const {
return value_ != kInvalid;
}
@@ -69,11 +94,6 @@ class Location : public ValueObject {
return !IsValid();
}
- bool IsConstant() const {
- // TODO: support constants.
- return false;
- }
-
// Empty location. Used if there the location should be ignored.
static Location NoLocation() {
return Location();
@@ -162,12 +182,13 @@ class Location : public ValueObject {
const char* DebugString() const {
switch (GetKind()) {
- case kInvalid: return "?";
+ case kInvalid: return "I";
case kRegister: return "R";
case kStackSlot: return "S";
case kDoubleStackSlot: return "DS";
case kQuickParameter: return "Q";
case kUnallocated: return "U";
+ case kConstant: return "C";
}
return "?";
}
@@ -196,6 +217,8 @@ class Location : public ValueObject {
return UnallocatedLocation(kRequiresRegister);
}
+ static Location RegisterOrConstant(HInstruction* instruction);
+
// The location of the first input to the instruction will be
// used to replace this unallocated location.
static Location SameAsFirstInput() {
@@ -215,6 +238,7 @@ class Location : public ValueObject {
// Number of bits required to encode Kind value.
static constexpr uint32_t kBitsForKind = 4;
static constexpr uint32_t kBitsForPayload = kWordSize * kBitsPerByte - kBitsForKind;
+ static constexpr uword kLocationTagMask = 0x3;
explicit Location(uword value) : value_(value) {}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index e87b044cc9..61a6f6b1b6 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -408,7 +408,7 @@ class HBasicBlock : public ArenaObject {
DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
};
-#define FOR_EACH_INSTRUCTION(M) \
+#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
M(Add) \
M(Condition) \
M(Equal) \
@@ -440,6 +440,9 @@ class HBasicBlock : public ArenaObject {
M(NullCheck) \
M(Temporary) \
+#define FOR_EACH_INSTRUCTION(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION(M) \
+ M(Constant)
#define FORWARD_DECLARATION(type) class H##type;
FOR_EACH_INSTRUCTION(FORWARD_DECLARATION)
@@ -1078,11 +1081,21 @@ class HStoreLocal : public HTemplateInstruction<2> {
DISALLOW_COPY_AND_ASSIGN(HStoreLocal);
};
+class HConstant : public HExpression<0> {
+ public:
+ explicit HConstant(Primitive::Type type) : HExpression(type) {}
+
+ DECLARE_INSTRUCTION(Constant);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HConstant);
+};
+
// Constants of the type int. Those can be from Dex instructions, or
// synthesized (for example with the if-eqz instruction).
-class HIntConstant : public HExpression<0> {
+class HIntConstant : public HConstant {
public:
- explicit HIntConstant(int32_t value) : HExpression(Primitive::kPrimInt), value_(value) {}
+ explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {}
int32_t GetValue() const { return value_; }
@@ -1094,14 +1107,12 @@ class HIntConstant : public HExpression<0> {
DISALLOW_COPY_AND_ASSIGN(HIntConstant);
};
-class HLongConstant : public HExpression<0> {
+class HLongConstant : public HConstant {
public:
- explicit HLongConstant(int64_t value) : HExpression(Primitive::kPrimLong), value_(value) {}
+ explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {}
int64_t GetValue() const { return value_; }
- virtual Primitive::Type GetType() const { return Primitive::kPrimLong; }
-
DECLARE_INSTRUCTION(LongConstant);
private:
@@ -1278,13 +1289,12 @@ class HPhi : public HInstruction {
DECLARE_INSTRUCTION(Phi);
- protected:
+ private:
GrowableArray<HInstruction*> inputs_;
const uint32_t reg_number_;
Primitive::Type type_;
bool is_live_;
- private:
DISALLOW_COPY_AND_ASSIGN(HPhi);
};
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b621e510f3..8a5077b962 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -79,13 +79,14 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite
jobject class_loader,
const DexFile& dex_file) const {
InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
- // The optimizing compiler currently does not have a Thumb2 assembler.
- if (instruction_set == kThumb2) {
- instruction_set = kArm;
+ // Always use the thumb2 assembler: some runtime functionality (like implicit stack
+ // overflow checks) assume thumb2.
+ if (instruction_set == kArm) {
+ instruction_set = kThumb2;
}
// Do not attempt to compile on architectures we do not support.
- if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kArm) {
+ if (instruction_set != kX86 && instruction_set != kX86_64 && instruction_set != kThumb2) {
return nullptr;
}
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 68130dd5fc..bd3a7d9767 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -153,13 +153,13 @@ void RegisterAllocator::AllocateRegistersInternal() {
if (current->HasRegister()) {
DCHECK(instruction->IsParameterValue());
inactive_.Add(current);
- } else if (current->HasSpillSlot()) {
- DCHECK(instruction->IsParameterValue());
+ } else if (current->HasSpillSlot() || instruction->IsConstant()) {
// Split before first register use.
size_t first_register_use = current->FirstRegisterUse();
if (first_register_use != kNoLifetime) {
LiveInterval* split = Split(current, first_register_use - 1);
- // The new interval may start at a late
+ // Don't add direclty to `unhandled_`, it needs to be sorted and the start
+ // of this new interval might be after intervals already in the list.
AddToUnhandled(split);
} else {
// Nothing to do, we won't allocate a register for this value.
@@ -579,6 +579,11 @@ void RegisterAllocator::AllocateSpillSlotFor(LiveInterval* interval) {
return;
}
+ if (defined_by->IsConstant()) {
+ // Constants don't need a spill slot.
+ return;
+ }
+
LiveInterval* last_sibling = interval;
while (last_sibling->GetNextSibling() != nullptr) {
last_sibling = last_sibling->GetNextSibling();
@@ -644,11 +649,16 @@ static Location ConvertToLocation(LiveInterval* interval) {
if (interval->HasRegister()) {
return Location::RegisterLocation(ManagedRegister(interval->GetRegister()));
} else {
- DCHECK(interval->GetParent()->HasSpillSlot());
- if (NeedTwoSpillSlot(interval->GetType())) {
- return Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot());
+ HInstruction* defined_by = interval->GetParent()->GetDefinedBy();
+ if (defined_by->IsConstant()) {
+ return defined_by->GetLocations()->Out();
} else {
- return Location::StackSlot(interval->GetParent()->GetSpillSlot());
+ DCHECK(interval->GetParent()->HasSpillSlot());
+ if (NeedTwoSpillSlot(interval->GetType())) {
+ return Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot());
+ } else {
+ return Location::StackSlot(interval->GetParent()->GetSpillSlot());
+ }
}
}
}
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index e35ff56c75..be1c7ec7c6 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -66,7 +66,10 @@ class RegisterAllocator {
static bool CanAllocateRegistersFor(const HGraph& graph, InstructionSet instruction_set);
static bool Supports(InstructionSet instruction_set) {
- return instruction_set == kX86 || instruction_set == kArm || instruction_set == kX86_64;
+ return instruction_set == kX86
+ || instruction_set == kArm
+ || instruction_set == kX86_64
+ || instruction_set == kThumb2;
}
size_t GetNumberOfSpillSlots() const {
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 8a34928a59..671ccb642c 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -73,6 +73,11 @@ std::ostream& operator<<(std::ostream& os, const Condition& rhs) {
return os;
}
+ShifterOperand::ShifterOperand(uint32_t immed)
+ : type_(kImmediate), rm_(kNoRegister), rs_(kNoRegister),
+ is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(immed) {
+ CHECK(immed < (1u << 12) || ArmAssembler::ModifiedImmediate(immed) != kInvalidModifiedImmediate);
+}
uint32_t ShifterOperand::encodingArm() const {
@@ -169,9 +174,7 @@ bool ShifterOperand::CanHoldThumb(Register rd, Register rn, Opcode opcode,
return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
case MOV:
- if (immediate < (1 << 12)) { // Less than (or equal to) 12 bits can always be done.
- return true;
- }
+ // TODO: Support less than or equal to 12bits.
return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
case MVN:
default:
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index be19174093..54965f678f 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -35,9 +35,7 @@ class ShifterOperand {
is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(0) {
}
- explicit ShifterOperand(uint32_t immed) : type_(kImmediate), rm_(kNoRegister), rs_(kNoRegister),
- is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(immed) {
- }
+ explicit ShifterOperand(uint32_t immed);
// Data-processing operands - Register
explicit ShifterOperand(Register rm) : type_(kRegister), rm_(rm), rs_(kNoRegister),
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 604f59e08f..78ff31a061 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -619,7 +619,8 @@ bool Thumb2Assembler::Is32BitDataProcessing(Condition cond,
return true;
}
- bool can_contain_high_register = opcode == MOV || opcode == ADD || opcode == SUB;
+ bool can_contain_high_register = (opcode == MOV)
+ || ((opcode == ADD || opcode == SUB) && (rn == rd));
if (IsHighRegister(rd) || IsHighRegister(rn)) {
if (can_contain_high_register) {
@@ -757,23 +758,21 @@ void Thumb2Assembler::Emit32BitDataProcessing(Condition cond,
int32_t encoding = 0;
if (so.IsImmediate()) {
// Check special cases.
- if ((opcode == SUB || opcode == ADD) && rn == SP) {
- // There are special ADD/SUB rd, SP, #imm12 instructions.
+ if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12))) {
if (opcode == SUB) {
thumb_opcode = 0b0101;
} else {
thumb_opcode = 0;
}
uint32_t imm = so.GetImmediate();
- CHECK_LT(imm, (1u << 12));
uint32_t i = (imm >> 11) & 1;
uint32_t imm3 = (imm >> 8) & 0b111;
uint32_t imm8 = imm & 0xff;
encoding = B31 | B30 | B29 | B28 | B25 |
- B19 | B18 | B16 |
thumb_opcode << 21 |
+ rn << 16 |
rd << 8 |
i << 26 |
imm3 << 12 |
@@ -877,11 +876,17 @@ void Thumb2Assembler::Emit16BitDataProcessing(Condition cond,
rn_shift = 8;
} else {
thumb_opcode = 0b1010;
+ rd = rn;
rn = so.GetRegister();
}
break;
- case CMN: thumb_opcode = 0b1011; rn = so.GetRegister(); break;
+ case CMN: {
+ thumb_opcode = 0b1011;
+ rd = rn;
+ rn = so.GetRegister();
+ break;
+ }
case ORR: thumb_opcode = 0b1100; break;
case MOV:
dp_opcode = 0;
@@ -1371,13 +1376,23 @@ void Thumb2Assembler::EmitLoadStore(Condition cond,
}
if (must_be_32bit) {
- int32_t encoding = 0x1f << 27 | B22 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 |
+ int32_t encoding = 0x1f << 27 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 |
ad.encodingThumb(true);
+ if (half) {
+ encoding |= B21;
+ } else if (!byte) {
+ encoding |= B22;
+ }
Emit32(encoding);
} else {
// 16 bit register offset.
int32_t encoding = B14 | B12 | (load ? B11 : 0) | static_cast<uint32_t>(rd) |
ad.encodingThumb(false);
+ if (byte) {
+ encoding |= B10;
+ } else if (half) {
+ encoding |= B9;
+ }
Emit16(encoding);
}
}
@@ -1470,6 +1485,7 @@ void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x
// branch the size may change if it so happens that other branches change size that change
// the distance to the target and that distance puts this branch over the limit for 16 bits.
if (size == Branch::k16Bit) {
+ DCHECK(!force_32bit_branches_);
Emit16(0); // Space for a 16 bit branch.
} else {
Emit32(0); // Space for a 32 bit branch.
@@ -1477,7 +1493,7 @@ void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x
} else {
// Branch is to an unbound label. Emit space for it.
uint16_t branch_id = AddBranch(branch_type, pc, cond); // Unresolved branch.
- if (force_32bit_) {
+ if (force_32bit_branches_ || force_32bit_) {
Emit16(static_cast<uint16_t>(label->position_)); // Emit current label link.
Emit16(0); // another 16 bits.
} else {
@@ -2073,6 +2089,7 @@ void Thumb2Assembler::Bind(Label* label) {
uint32_t branch_location = branch->GetLocation();
uint16_t next = buffer_.Load<uint16_t>(branch_location); // Get next in chain.
if (changed) {
+ DCHECK(!force_32bit_branches_);
MakeHoleForBranch(branch->GetLocation(), 2);
if (branch->IsCompareAndBranch()) {
// A cbz/cbnz instruction has changed size. There is no valid encoding for
@@ -2506,12 +2523,22 @@ void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) {
void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) {
- cbz(r, label);
+ if (force_32bit_branches_) {
+ cmp(r, ShifterOperand(0));
+ b(label, EQ);
+ } else {
+ cbz(r, label);
+ }
}
void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) {
- cbnz(r, label);
+ if (force_32bit_branches_) {
+ cmp(r, ShifterOperand(0));
+ b(label, NE);
+ } else {
+ cbnz(r, label);
+ }
}
} // namespace arm
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 5f24e4e0e1..ee33bf23c9 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -29,10 +29,13 @@
namespace art {
namespace arm {
-
class Thumb2Assembler FINAL : public ArmAssembler {
public:
- Thumb2Assembler() : force_32bit_(false), it_cond_index_(kNoItCondition), next_condition_(AL) {
+ explicit Thumb2Assembler(bool force_32bit_branches = false)
+ : force_32bit_branches_(force_32bit_branches),
+ force_32bit_(false),
+ it_cond_index_(kNoItCondition),
+ next_condition_(AL) {
}
virtual ~Thumb2Assembler() {
@@ -49,6 +52,10 @@ class Thumb2Assembler FINAL : public ArmAssembler {
return force_32bit_;
}
+ bool IsForced32BitBranches() const {
+ return force_32bit_branches_;
+ }
+
void FinalizeInstructions(const MemoryRegion& region) OVERRIDE {
EmitBranches();
Assembler::FinalizeInstructions(region);
@@ -412,7 +419,8 @@ class Thumb2Assembler FINAL : public ArmAssembler {
void EmitShift(Register rd, Register rm, Shift shift, uint8_t amount, bool setcc = false);
void EmitShift(Register rd, Register rn, Shift shift, Register rm, bool setcc = false);
- bool force_32bit_; // Force the assembler to use 32 bit thumb2 instructions.
+ bool force_32bit_branches_; // Force the assembler to use 32 bit branch instructions.
+ bool force_32bit_; // Force the assembler to use 32 bit thumb2 instructions.
// IfThen conditions. Used to check that conditional instructions match the preceding IT.
Condition it_conditions_[4];
@@ -605,6 +613,9 @@ class Thumb2Assembler FINAL : public ArmAssembler {
private:
// Calculate the size of the branch instruction based on its type and offset.
Size CalculateSize() const {
+ if (assembler_->IsForced32BitBranches()) {
+ return k32Bit;
+ }
if (target_ == kUnresolved) {
if (assembler_->IsForced32Bit() && (type_ == kUnconditional || type_ == kConditional)) {
return k32Bit;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 68cb6569b1..3312f8f21a 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -14,7 +14,10 @@
* limitations under the License.
*/
+#include <dirent.h>
#include <fstream>
+#include <sys/types.h>
+#include <map>
#include "gtest/gtest.h"
#include "utils/arm/assembler_thumb2.h"
@@ -40,6 +43,8 @@ namespace arm {
static constexpr bool kPrintResults = false;
#endif
+static const char* TOOL_PREFIX = "arm-linux-androideabi-";
+
void SetAndroidData() {
const char* data = getenv("ANDROID_DATA");
if (data == nullptr) {
@@ -109,9 +114,9 @@ std::string GetAndroidToolsDir() {
// Suffix on toolsdir will be something like "arm-eabi-4.8"
while ((entry = readdir(dir)) != nullptr) {
std::string subdir = toolsdir + std::string("/") + std::string(entry->d_name);
- size_t eabi = subdir.find("arm-eabi-");
+ size_t eabi = subdir.find(TOOL_PREFIX);
if (eabi != std::string::npos) {
- std::string suffix = subdir.substr(eabi + sizeof("arm-eabi-"));
+ std::string suffix = subdir.substr(eabi + strlen(TOOL_PREFIX));
double version = strtod(suffix.c_str(), nullptr);
if (version > maxversion) {
maxversion = version;
@@ -166,22 +171,22 @@ void dump(std::vector<uint8_t>& code, const char* testname) {
}
out.close();
- char cmd[256];
+ char cmd[1024];
// Assemble the .S
- snprintf(cmd, sizeof(cmd), "%sarm-eabi-as %s -o %s.o", toolsdir.c_str(), filename, filename);
+ snprintf(cmd, sizeof(cmd), "%s%sas %s -o %s.o", toolsdir.c_str(), TOOL_PREFIX, filename, filename);
system(cmd);
// Remove the $d symbols to prevent the disassembler dumping the instructions
// as .word
- snprintf(cmd, sizeof(cmd), "%sarm-eabi-objcopy -N '$d' %s.o %s.oo", toolsdir.c_str(),
+ snprintf(cmd, sizeof(cmd), "%s%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), TOOL_PREFIX,
filename, filename);
system(cmd);
// Disassemble.
- snprintf(cmd, sizeof(cmd), "%sarm-eabi-objdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'",
- toolsdir.c_str(), filename);
+ snprintf(cmd, sizeof(cmd), "%s%sobjdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'",
+ toolsdir.c_str(), TOOL_PREFIX, filename);
if (kPrintResults) {
// Print the results only, don't check. This is used to generate new output for inserting
// into the .inc file.
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 3943e3713b..18035f391c 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -48,8 +48,8 @@ const char* DataProcessingRegisterResults[] = {
const char* DataProcessingImmediateResults[] = {
" 0: 2055 movs r0, #85 ; 0x55\n",
" 2: f06f 0055 mvn.w r0, #85 ; 0x55\n",
- " 6: f101 0055 add.w r0, r1, #85 ; 0x55\n",
- " a: f1a1 0055 sub.w r0, r1, #85 ; 0x55\n",
+ " 6: f201 0055 addw r0, r1, #85 ; 0x55\n",
+ " a: f2a1 0055 subw r0, r1, #85 ; 0x55\n",
" e: f001 0055 and.w r0, r1, #85 ; 0x55\n",
" 12: f041 0055 orr.w r0, r1, #85 ; 0x55\n",
" 16: f081 0055 eor.w r0, r1, #85 ; 0x55\n",
diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h
new file mode 100644
index 0000000000..63adbc2328
--- /dev/null
+++ b/compiler/utils/stack_checks.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_STACK_CHECKS_H_
+#define ART_COMPILER_UTILS_STACK_CHECKS_H_
+
+#include "instruction_set.h"
+
+namespace art {
+
+// Size of a frame that we definitely consider large. Anything larger than this should
+// definitely get a stack overflow check.
+static constexpr size_t kLargeFrameSize = 2 * KB;
+
+// Size of a frame that should be small. Anything leaf method smaller than this should run
+// without a stack overflow check.
+// The constant is from experience with frameworks code.
+static constexpr size_t kSmallFrameSize = 1 * KB;
+
+// Determine whether a frame is small or large, used in the decision on whether to elide a
+// stack overflow check on method entry.
+//
+// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the
+// overflow-usable stack space.
+static inline bool IsLargeFrame(size_t size, InstructionSet isa) {
+ return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4;
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_UTILS_STACK_CHECKS_H_
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 56c6536fe5..b6a5c20cb8 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -806,6 +806,13 @@ void X86Assembler::testl(Register reg1, Register reg2) {
}
+void X86Assembler::testl(Register reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x85);
+ EmitOperand(reg, address);
+}
+
+
void X86Assembler::testl(Register reg, const Immediate& immediate) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
// For registers that have a byte variant (EAX, EBX, ECX, and EDX)
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 2fc6049042..ce20768c26 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -346,6 +346,7 @@ class X86Assembler FINAL : public Assembler {
void testl(Register reg1, Register reg2);
void testl(Register reg, const Immediate& imm);
+ void testl(Register reg1, const Address& address);
void andl(Register dst, const Immediate& imm);
void andl(Register dst, Register src);
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 78738d8934..0d14376bf3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -869,6 +869,22 @@ void X86_64Assembler::cmpq(CpuRegister reg0, CpuRegister reg1) {
}
+void X86_64Assembler::cmpq(CpuRegister reg, const Immediate& imm) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ CHECK(imm.is_int32()); // cmpq only supports 32b immediate.
+ EmitRex64(reg);
+ EmitComplex(7, Operand(reg), imm);
+}
+
+
+void X86_64Assembler::cmpq(CpuRegister reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(reg);
+ EmitUint8(0x3B);
+ EmitOperand(reg.LowBits(), address);
+}
+
+
void X86_64Assembler::addl(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(dst, src);
@@ -934,6 +950,14 @@ void X86_64Assembler::testl(CpuRegister reg, const Immediate& immediate) {
}
+void X86_64Assembler::testq(CpuRegister reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(reg);
+ EmitUint8(0x85);
+ EmitOperand(reg.LowBits(), address);
+}
+
+
void X86_64Assembler::andl(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(dst, src);
@@ -1063,6 +1087,14 @@ void X86_64Assembler::addq(CpuRegister reg, const Immediate& imm) {
}
+void X86_64Assembler::addq(CpuRegister dst, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(dst);
+ EmitUint8(0x03);
+ EmitOperand(dst.LowBits(), address);
+}
+
+
void X86_64Assembler::addq(CpuRegister dst, CpuRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
// 0x01 is addq r/m64 <- r/m64 + r64, with op1 in r/m and op2 in reg: so reverse EmitRex64
@@ -1118,6 +1150,14 @@ void X86_64Assembler::subq(CpuRegister dst, CpuRegister src) {
}
+void X86_64Assembler::subq(CpuRegister reg, const Address& address) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitRex64(reg);
+ EmitUint8(0x2B);
+ EmitOperand(reg.LowBits() & 7, address);
+}
+
+
void X86_64Assembler::subl(CpuRegister reg, const Address& address) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(reg, address);
@@ -1201,7 +1241,7 @@ void X86_64Assembler::mull(const Address& address) {
void X86_64Assembler::shll(CpuRegister reg, const Immediate& imm) {
- EmitGenericShift(4, reg, imm);
+ EmitGenericShift(false, 4, reg, imm);
}
@@ -1211,7 +1251,12 @@ void X86_64Assembler::shll(CpuRegister operand, CpuRegister shifter) {
void X86_64Assembler::shrl(CpuRegister reg, const Immediate& imm) {
- EmitGenericShift(5, reg, imm);
+ EmitGenericShift(false, 5, reg, imm);
+}
+
+
+void X86_64Assembler::shrq(CpuRegister reg, const Immediate& imm) {
+ EmitGenericShift(true, 5, reg, imm);
}
@@ -1221,7 +1266,7 @@ void X86_64Assembler::shrl(CpuRegister operand, CpuRegister shifter) {
void X86_64Assembler::sarl(CpuRegister reg, const Immediate& imm) {
- EmitGenericShift(7, reg, imm);
+ EmitGenericShift(false, 7, reg, imm);
}
@@ -1537,11 +1582,15 @@ void X86_64Assembler::EmitLabelLink(Label* label) {
}
-void X86_64Assembler::EmitGenericShift(int reg_or_opcode,
- CpuRegister reg,
- const Immediate& imm) {
+void X86_64Assembler::EmitGenericShift(bool wide,
+ int reg_or_opcode,
+ CpuRegister reg,
+ const Immediate& imm) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
CHECK(imm.is_int8());
+ if (wide) {
+ EmitRex64(reg);
+ }
if (imm.value() == 1) {
EmitUint8(0xD1);
EmitOperand(reg_or_opcode, Operand(reg));
@@ -1554,8 +1603,8 @@ void X86_64Assembler::EmitGenericShift(int reg_or_opcode,
void X86_64Assembler::EmitGenericShift(int reg_or_opcode,
- CpuRegister operand,
- CpuRegister shifter) {
+ CpuRegister operand,
+ CpuRegister shifter) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
CHECK_EQ(shifter.AsRegister(), RCX);
EmitUint8(0xD3);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 7514854829..1d6655c24e 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -385,10 +385,14 @@ class X86_64Assembler FINAL : public Assembler {
void cmpl(const Address& address, const Immediate& imm);
void cmpq(CpuRegister reg0, CpuRegister reg1);
+ void cmpq(CpuRegister reg0, const Immediate& imm);
+ void cmpq(CpuRegister reg0, const Address& address);
void testl(CpuRegister reg1, CpuRegister reg2);
void testl(CpuRegister reg, const Immediate& imm);
+ void testq(CpuRegister reg, const Address& address);
+
void andl(CpuRegister dst, const Immediate& imm);
void andl(CpuRegister dst, CpuRegister src);
void andq(CpuRegister dst, const Immediate& imm);
@@ -408,6 +412,7 @@ class X86_64Assembler FINAL : public Assembler {
void addq(CpuRegister reg, const Immediate& imm);
void addq(CpuRegister dst, CpuRegister src);
+ void addq(CpuRegister dst, const Address& address);
void subl(CpuRegister dst, CpuRegister src);
void subl(CpuRegister reg, const Immediate& imm);
@@ -415,6 +420,7 @@ class X86_64Assembler FINAL : public Assembler {
void subq(CpuRegister reg, const Immediate& imm);
void subq(CpuRegister dst, CpuRegister src);
+ void subq(CpuRegister dst, const Address& address);
void cdq();
@@ -437,6 +443,8 @@ class X86_64Assembler FINAL : public Assembler {
void sarl(CpuRegister reg, const Immediate& imm);
void sarl(CpuRegister operand, CpuRegister shifter);
+ void shrq(CpuRegister reg, const Immediate& imm);
+
void negl(CpuRegister reg);
void notl(CpuRegister reg);
@@ -622,7 +630,7 @@ class X86_64Assembler FINAL : public Assembler {
void EmitLabelLink(Label* label);
void EmitNearLabelLink(Label* label);
- void EmitGenericShift(int rm, CpuRegister reg, const Immediate& imm);
+ void EmitGenericShift(bool wide, int rm, CpuRegister reg, const Immediate& imm);
void EmitGenericShift(int rm, CpuRegister operand, CpuRegister shifter);
// If any input is not false, output the necessary rex prefix.
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6d861d4bfe..d6501a162d 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -45,6 +45,7 @@
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "elf_fixup.h"
+#include "elf_patcher.h"
#include "elf_stripper.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -324,11 +325,28 @@ class Dex2Oat {
return ReadImageClasses(image_classes_stream);
}
+ bool PatchOatCode(const CompilerDriver* compiler_driver, File* oat_file,
+ const std::string& oat_location, std::string* error_msg) {
+ // We asked to include patch information but we are not making an image. We need to fix
+ // everything up manually.
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(oat_file, PROT_READ|PROT_WRITE,
+ MAP_SHARED, error_msg));
+ if (elf_file.get() == NULL) {
+ LOG(ERROR) << error_msg;
+ return false;
+ }
+ {
+ ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
+ return ElfPatcher::Patch(compiler_driver, elf_file.get(), oat_location, error_msg);
+ }
+ }
+
const CompilerDriver* CreateOatFile(const std::string& boot_image_option,
const std::string& android_root,
bool is_host,
const std::vector<const DexFile*>& dex_files,
File* oat_file,
+ const std::string& oat_location,
const std::string& bitcode_filename,
bool image,
std::unique_ptr<CompilerDriver::DescriptorSet>& image_classes,
@@ -380,6 +398,7 @@ class Dex2Oat {
std::string image_file_location;
uint32_t image_file_location_oat_checksum = 0;
uintptr_t image_file_location_oat_data_begin = 0;
+ int32_t image_patch_delta = 0;
if (!driver->IsImage()) {
TimingLogger::ScopedTiming t3("Loading image checksum", &timings);
gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
@@ -387,6 +406,7 @@ class Dex2Oat {
image_file_location_oat_data_begin =
reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
image_file_location = image_space->GetImageFilename();
+ image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
}
if (!image_file_location.empty()) {
@@ -395,6 +415,7 @@ class Dex2Oat {
OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
image_file_location_oat_data_begin,
+ image_patch_delta,
driver.get(),
&timings,
key_value_store);
@@ -405,6 +426,16 @@ class Dex2Oat {
return nullptr;
}
+ if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
+ t2.NewTiming("Patching ELF");
+ std::string error_msg;
+ if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
+ LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
+ LOG(ERROR) << "Error was: " << error_msg;
+ return nullptr;
+ }
+ }
+
return driver.release();
}
@@ -1361,6 +1392,7 @@ static int dex2oat(int argc, char** argv) {
is_host,
dex_files,
oat_file.get(),
+ oat_location,
bitcode_filename,
image,
image_classes,
@@ -1370,7 +1402,6 @@ static int dex2oat(int argc, char** argv) {
compiler_phases_timings,
profile_file,
key_value_store.get()));
-
if (compiler.get() == nullptr) {
LOG(ERROR) << "Failed to create oat file: " << oat_location;
return EXIT_FAILURE;
@@ -1420,9 +1451,9 @@ static int dex2oat(int argc, char** argv) {
// memory mapped so we could predict where its contents were based
// on the file size. Now that it is an ELF file, we need to inspect
// the ELF file to understand the in memory segment layout including
- // where the oat header is located within. ImageWriter's
- // PatchOatCodeAndMethods uses the PatchInformation from the
- // Compiler to touch up absolute references in the oat file.
+ // where the oat header is located within. ElfPatcher's Patch method
+ // uses the PatchInformation from the Compiler to touch up absolute
+ // references in the oat file.
//
// 3. We fixup the ELF program headers so that dlopen will try to
// load the .so at the desired location at runtime by offsetting the
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b8f20f3650..068a450426 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -165,6 +165,8 @@ class OatDumper {
GetQuickToInterpreterBridgeOffset);
#undef DUMP_OAT_HEADER_OFFSET
+ os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta();
+
os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
@@ -771,6 +773,8 @@ class ImageDumper {
os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
+ os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
+
{
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
@@ -819,10 +823,13 @@ class ImageDumper {
os << "OAT LOCATION: " << oat_location;
os << "\n";
std::string error_msg;
- const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location, &error_msg);
- if (oat_file == NULL) {
- os << "NOT FOUND: " << error_msg << "\n";
- return;
+ const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+ if (oat_file == nullptr) {
+ oat_file = OatFile::Open(oat_location, oat_location, NULL, false, &error_msg);
+ if (oat_file == nullptr) {
+ os << "NOT FOUND: " << error_msg << "\n";
+ return;
+ }
}
os << "\n";
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 85b4e6df32..4c88c9ea6d 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -17,11 +17,14 @@
#include <stdio.h>
#include <stdlib.h>
+#include <sys/file.h>
#include <sys/stat.h>
+#include <unistd.h>
#include <string>
#include <vector>
+#include "base/scoped_flock.h"
#include "base/stringpiece.h"
#include "base/stringprintf.h"
#include "elf_utils.h"
@@ -125,7 +128,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta,
delta, timings);
t.NewTiming("Patching files");
if (!p.PatchImage()) {
- LOG(INFO) << "Failed to patch image file " << input_image->GetPath();
+ LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
return false;
}
@@ -216,11 +219,11 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o
delta, timings);
t.NewTiming("Patching files");
if (!p.PatchElf()) {
- LOG(INFO) << "Failed to patch oat file " << input_oat->GetPath();
+ LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath();
return false;
}
if (!p.PatchImage()) {
- LOG(INFO) << "Failed to patch image file " << input_image->GetPath();
+ LOG(ERROR) << "Failed to patch image file " << input_image->GetPath();
return false;
}
@@ -236,6 +239,12 @@ bool PatchOat::Patch(const File* input_oat, const std::string& image_location, o
bool PatchOat::WriteElf(File* out) {
TimingLogger::ScopedTiming t("Writing Elf File", timings_);
+ std::string error_msg;
+
+ // Lock the output file.
+ ScopedFlock flock;
+ flock.Init(out, &error_msg);
+
CHECK(oat_file_.get() != nullptr);
CHECK(out != nullptr);
size_t expect = oat_file_->Size();
@@ -250,6 +259,12 @@ bool PatchOat::WriteElf(File* out) {
bool PatchOat::WriteImage(File* out) {
TimingLogger::ScopedTiming t("Writing image File", timings_);
+ std::string error_msg;
+
+ // Lock the output file.
+ ScopedFlock flock;
+ flock.Init(out, &error_msg);
+
CHECK(image_ != nullptr);
CHECK(out != nullptr);
size_t expect = image_->Size();
@@ -437,19 +452,50 @@ bool PatchOat::CheckOatFile() {
return true;
}
+bool PatchOat::PatchOatHeader() {
+ Elf32_Shdr *rodata_sec = oat_file_->FindSectionByName(".rodata");
+ if (rodata_sec == nullptr) {
+ return false;
+ }
+ OatHeader* oat_header = reinterpret_cast<OatHeader*>(oat_file_->Begin() + rodata_sec->sh_offset);
+ if (!oat_header->IsValid()) {
+ LOG(ERROR) << "Elf file " << oat_file_->GetFile().GetPath() << " has an invalid oat header";
+ return false;
+ }
+ oat_header->RelocateOat(delta_);
+ return true;
+}
+
bool PatchOat::PatchElf() {
- TimingLogger::ScopedTiming t("Fixup Elf Headers", timings_);
+ TimingLogger::ScopedTiming t("Fixup Elf Text Section", timings_);
+ if (!PatchTextSection()) {
+ return false;
+ }
+
+ if (!PatchOatHeader()) {
+ return false;
+ }
+
+ bool need_fixup = false;
+ t.NewTiming("Fixup Elf Headers");
// Fixup Phdr's
for (unsigned int i = 0; i < oat_file_->GetProgramHeaderNum(); i++) {
Elf32_Phdr& hdr = oat_file_->GetProgramHeader(i);
- if (hdr.p_vaddr != 0) {
+ if (hdr.p_vaddr != 0 && hdr.p_vaddr != hdr.p_offset) {
+ need_fixup = true;
hdr.p_vaddr += delta_;
}
- if (hdr.p_paddr != 0) {
+ if (hdr.p_paddr != 0 && hdr.p_paddr != hdr.p_offset) {
+ need_fixup = true;
hdr.p_paddr += delta_;
}
}
- // Fixup Shdr's
+ if (!need_fixup) {
+ // This was never passed through ElfFixup so all headers/symbols just have their offset as
+ // their addr. Therefore we do not need to update these parts.
+ return true;
+ }
+ t.NewTiming("Fixup Section Headers");
for (unsigned int i = 0; i < oat_file_->GetSectionHeaderNum(); i++) {
Elf32_Shdr& hdr = oat_file_->GetSectionHeader(i);
if (hdr.sh_addr != 0) {
@@ -457,7 +503,7 @@ bool PatchOat::PatchElf() {
}
}
- // Fixup Dynamics.
+ t.NewTiming("Fixup Dynamics");
for (Elf32_Word i = 0; i < oat_file_->GetDynamicNum(); i++) {
Elf32_Dyn& dyn = oat_file_->GetDynamic(i);
if (IsDynamicSectionPointer(dyn.d_tag, oat_file_->GetHeader().e_machine)) {
@@ -481,12 +527,6 @@ bool PatchOat::PatchElf() {
}
}
- t.NewTiming("Fixup Elf Text Section");
- // Fixup text
- if (!PatchTextSection()) {
- return false;
- }
-
return true;
}
@@ -511,7 +551,7 @@ bool PatchOat::PatchSymbols(Elf32_Shdr* section) {
bool PatchOat::PatchTextSection() {
Elf32_Shdr* patches_sec = oat_file_->FindSectionByName(".oat_patches");
if (patches_sec == nullptr) {
- LOG(INFO) << ".oat_patches section not found. Aborting patch";
+ LOG(ERROR) << ".oat_patches section not found. Aborting patch";
return false;
}
DCHECK(CheckOatFile()) << "Oat file invalid";
@@ -614,7 +654,8 @@ static void Usage(const char *fmt, ...) {
UsageError("");
UsageError(" --patched-image-location=<file.art>: Use the same patch delta as was used to");
UsageError(" patch the given image location. If used one must also specify the");
- UsageError(" --instruction-set flag.");
+ UsageError(" --instruction-set flag. It will search for this image in the same way that");
+ UsageError(" is done when loading one.");
UsageError("");
UsageError(" --dump-timings: dump out patch timing information");
UsageError("");
@@ -909,7 +950,25 @@ static int patchoat(int argc, char **argv) {
if (!isa_set) {
Usage("specifying a location requires specifying an instruction set");
}
- patched_image_filename = GetSystemImageFilename(patched_image_location.c_str(), isa);
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool has_android_data_unused = false;
+ if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa,
+ &system_filename, &has_system, &cache_filename,
+ &has_android_data_unused, &has_cache)) {
+ Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+ }
+ if (has_cache) {
+ patched_image_filename = cache_filename;
+ } else if (has_system) {
+ LOG(WARNING) << "Only image file found was in /system for image location "
+ << patched_image_location;
+ patched_image_filename = system_filename;
+ } else {
+ Usage("Unable to determine image file for location %s", patched_image_location.c_str());
+ }
if (debug) {
LOG(INFO) << "Using patched-image-file " << patched_image_filename;
}
@@ -965,10 +1024,21 @@ static int patchoat(int argc, char **argv) {
} else {
CHECK(!input_oat_filename.empty());
input_oat.reset(OS::OpenFileForReading(input_oat_filename.c_str()));
+ if (input_oat.get() == nullptr) {
+ LOG(ERROR) << "Could not open input oat file: " << strerror(errno);
+ }
}
if (output_oat_fd != -1) {
output_oat.reset(new File(output_oat_fd, output_oat_filename));
+ } else if (output_oat_filename == input_oat_filename) {
+ // This could be a wierd situation, since we'd be writting from an mmap'd copy of this file.
+ // Lets just unlink it.
+ if (0 != unlink(input_oat_filename.c_str())) {
+ PLOG(ERROR) << "Could not unlink " << input_oat_filename << " to make room for output";
+ return false;
+ }
+ output_oat.reset(OS::CreateEmptyFile(output_oat_filename.c_str()));
} else {
CHECK(!output_oat_filename.empty());
output_oat.reset(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out));
@@ -994,7 +1064,9 @@ static int patchoat(int argc, char **argv) {
};
if (debug) {
- LOG(INFO) << "moving offset by " << base_delta << " (0x" << std::hex << base_delta << ") bytes";
+ LOG(INFO) << "moving offset by " << base_delta
+ << " (0x" << std::hex << base_delta << ") bytes or "
+ << std::dec << (base_delta/kPageSize) << " pages.";
}
bool ret;
@@ -1011,6 +1083,7 @@ static int patchoat(int argc, char **argv) {
ret = PatchOat::Patch(input_image_location, base_delta, output_image.get(), isa, &timings);
}
cleanup(ret);
+ sync();
return (ret) ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h
index a63e6f44b8..6960d3b446 100644
--- a/patchoat/patchoat.h
+++ b/patchoat/patchoat.h
@@ -79,6 +79,7 @@ class PatchOat {
// Patches oat in place, modifying the oat_file given to the constructor.
bool PatchElf();
bool PatchTextSection();
+ bool PatchOatHeader();
bool PatchSymbols(Elf32_Shdr* section);
bool PatchImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index d2fc2298e2..6fa34c40ab 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -238,6 +238,7 @@ LIBART_SRC_FILES_x86 := \
arch/x86/context_x86.cc \
arch/x86/entrypoints_init_x86.cc \
arch/x86/jni_entrypoints_x86.S \
+ arch/x86/memcmp16_x86.S \
arch/x86/portable_entrypoints_x86.S \
arch/x86/quick_entrypoints_x86.S \
arch/x86/thread_x86.cc \
@@ -292,6 +293,7 @@ LIBART_ENUM_OPERATOR_OUT_HEADER_FILES := \
dex_file.h \
dex_instruction.h \
gc/collector/gc_type.h \
+ gc/collector_type.h \
gc/space/space.h \
gc/heap.h \
indirect_reference_table.h \
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index e22c56ec69..48582f464e 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -61,7 +61,7 @@ void FaultManager::GetMethodAndReturnPCAndSP(siginfo_t* siginfo, void* context,
// get the method from the top of the stack. However it's in r0.
uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(*out_sp) - kArmStackOverflowReservedBytes);
+ reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(kArm));
if (overflow_addr == fault_addr) {
*out_method = reinterpret_cast<mirror::ArtMethod*>(sc->arm_r0);
} else {
@@ -192,7 +192,7 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) {
VLOG(signals) << "checking for stack overflow, sp: " << std::hex << sp <<
", fault_addr: " << fault_addr;
- uintptr_t overflow_addr = sp - kArmStackOverflowReservedBytes;
+ uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm);
Thread* self = reinterpret_cast<Thread*>(sc->arm_r9);
CHECK_EQ(self, Thread::Current());
diff --git a/runtime/arch/memcmp16.h b/runtime/arch/memcmp16.h
index 1144c8c89d..65d2f92d74 100644
--- a/runtime/arch/memcmp16.h
+++ b/runtime/arch/memcmp16.h
@@ -30,7 +30,7 @@
//
// In both cases, MemCmp16 is declared.
-#if defined(__aarch64__) || defined(__arm__) || defined(__mips)
+#if defined(__aarch64__) || defined(__arm__) || defined(__mips) || defined(__i386__)
extern "C" uint32_t __memcmp16(const uint16_t* s0, const uint16_t* s1, size_t count);
#define MemCmp16 __memcmp16
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index ada1523ba3..8786222250 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1076,14 +1076,15 @@ art_quick_instrumentation_exit:
.cfi_startproc
addiu $t9, $ra, 4 # put current address into $t9 to rebuild $gp
GENERATE_GLOBAL_POINTER
- move $t0, $sp # remember bottom of caller's frame
+ move $ra, $zero # link register is to here, so clobber with 0 for later checks
SETUP_REF_ONLY_CALLEE_SAVE_FRAME
+ move $t0, $sp # remember bottom of caller's frame
addiu $sp, $sp, -48 # save return values and set up args
.cfi_adjust_cfa_offset 48
sw $v0, 32($sp)
- .cfi_rel_offset 2, 0
+ .cfi_rel_offset 2, 32
sw $v1, 36($sp)
- .cfi_rel_offset 3, 4
+ .cfi_rel_offset 3, 36
s.s $f0, 40($sp)
s.s $f1, 44($sp)
s.s $f0, 16($sp) # pass fpr result
diff --git a/runtime/arch/x86/asm_support_x86.S b/runtime/arch/x86/asm_support_x86.S
index ae39be13d8..e468c2a8d7 100644
--- a/runtime/arch/x86/asm_support_x86.S
+++ b/runtime/arch/x86/asm_support_x86.S
@@ -81,6 +81,8 @@
#define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
#define CFI_RESTORE(reg) .cfi_restore reg
#define CFI_REL_OFFSET(reg,size) .cfi_rel_offset reg,size
+ #define CFI_RESTORE_STATE .cfi_restore_state
+ #define CFI_REMEMBER_STATE .cfi_remember_state
#else
// Mac OS' doesn't like cfi_* directives.
#define CFI_STARTPROC
@@ -90,6 +92,8 @@
#define CFI_DEF_CFA_REGISTER(reg)
#define CFI_RESTORE(reg)
#define CFI_REL_OFFSET(reg,size)
+ #define CFI_RESTORE_STATE
+ #define CFI_REMEMBER_STATE
#endif
// Symbols.
diff --git a/runtime/arch/x86/memcmp16_x86.S b/runtime/arch/x86/memcmp16_x86.S
new file mode 100644
index 0000000000..17662fae6b
--- /dev/null
+++ b/runtime/arch/x86/memcmp16_x86.S
@@ -0,0 +1,1038 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asm_support_x86.S"
+
+#define MEMCMP __memcmp16
+
+/* int32_t memcmp16_compare(const uint16_t* s0, const uint16_t* s1, size_t count); */
+
+#ifndef L
+# define L(label) .L##label
+#endif
+
+#define CFI_PUSH(REG) \
+ CFI_ADJUST_CFA_OFFSET(4); \
+ CFI_REL_OFFSET(REG, 0)
+
+#define CFI_POP(REG) \
+ CFI_ADJUST_CFA_OFFSET(-4); \
+ CFI_RESTORE(REG)
+
+#define PUSH(REG) pushl REG; CFI_PUSH (REG)
+#define POP(REG) popl REG; CFI_POP (REG)
+
+#define PARMS 4
+#define BLK1 PARMS
+#define BLK2 BLK1+4
+#define LEN BLK2+4
+#define RETURN_END POP (%edi); POP (%esi); POP (%ebx); ret
+#define RETURN RETURN_END; CFI_RESTORE_STATE; CFI_REMEMBER_STATE
+
+DEFINE_FUNCTION MEMCMP
+ movl LEN(%esp), %ecx
+
+ shl $1, %ecx
+ jz L(zero)
+
+ movl BLK1(%esp), %eax
+ cmp $48, %ecx
+ movl BLK2(%esp), %edx
+ jae L(48bytesormore)
+
+ PUSH (%ebx)
+ add %ecx, %edx
+ add %ecx, %eax
+ jmp L(less48bytes)
+
+ CFI_POP (%ebx)
+
+ .p2align 4
+L(zero):
+ xor %eax, %eax
+ ret
+
+ .p2align 4
+L(48bytesormore):
+ PUSH (%ebx)
+ PUSH (%esi)
+ PUSH (%edi)
+ CFI_REMEMBER_STATE
+ movdqu (%eax), %xmm3
+ movdqu (%edx), %xmm0
+ movl %eax, %edi
+ movl %edx, %esi
+ pcmpeqb %xmm0, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 16(%edi), %edi
+
+ sub $0xffff, %edx
+ lea 16(%esi), %esi
+ jnz L(less16bytes)
+ mov %edi, %edx
+ and $0xf, %edx
+ xor %edx, %edi
+ sub %edx, %esi
+ add %edx, %ecx
+ mov %esi, %edx
+ and $0xf, %edx
+ jz L(shr_0)
+ xor %edx, %esi
+
+ cmp $0, %edx
+ je L(shr_0)
+ cmp $2, %edx
+ je L(shr_2)
+ cmp $4, %edx
+ je L(shr_4)
+ cmp $6, %edx
+ je L(shr_6)
+ cmp $8, %edx
+ je L(shr_8)
+ cmp $10, %edx
+ je L(shr_10)
+ cmp $12, %edx
+ je L(shr_12)
+ jmp L(shr_14)
+
+ .p2align 4
+L(shr_0):
+ cmp $80, %ecx
+ jae L(shr_0_gobble)
+ lea -48(%ecx), %ecx
+ xor %eax, %eax
+ movaps (%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+ movaps 16(%esi), %xmm2
+ pcmpeqb 16(%edi), %xmm2
+ pand %xmm1, %xmm2
+ pmovmskb %xmm2, %edx
+ add $32, %edi
+ add $32, %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea (%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_0_gobble):
+ lea -48(%ecx), %ecx
+ movdqa (%esi), %xmm0
+ xor %eax, %eax
+ pcmpeqb (%edi), %xmm0
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm2
+ pcmpeqb 16(%edi), %xmm2
+L(shr_0_gobble_loop):
+ pand %xmm0, %xmm2
+ sub $32, %ecx
+ pmovmskb %xmm2, %edx
+ movdqa %xmm0, %xmm1
+ movdqa 32(%esi), %xmm0
+ movdqa 48(%esi), %xmm2
+ sbb $0xffff, %edx
+ pcmpeqb 32(%edi), %xmm0
+ pcmpeqb 48(%edi), %xmm2
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ jz L(shr_0_gobble_loop)
+
+ pand %xmm0, %xmm2
+ cmp $0, %ecx
+ jge L(shr_0_gobble_loop_next)
+ inc %edx
+ add $32, %ecx
+L(shr_0_gobble_loop_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm2, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea (%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_2):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_2_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $2,(%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $2,%xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 2(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_2_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $2,(%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $2,16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_2_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $2,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $2,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_2_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_2_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_2_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 2(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_4):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_4_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $4,(%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $4,%xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 4(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_4_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $4,(%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $4,16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_4_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $4,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $4,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_4_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_4_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_4_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 4(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_6):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_6_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $6,(%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $6,%xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 6(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_6_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $6,(%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $6,16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_6_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $6,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $6,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_6_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_6_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_6_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 6(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_8):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_8_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $8,(%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $8,%xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 8(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_8_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $8,(%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $8,16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_8_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $8,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $8,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_8_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_8_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_8_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 8(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_10):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_10_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $10, (%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $10,%xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 10(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_10_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $10, (%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $10, 16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_10_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $10,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $10,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_10_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_10_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_10_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 10(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_12):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_12_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $12, (%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $12, %xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 12(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_12_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $12, (%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $12, 16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_12_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $12,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $12,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_12_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_12_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_12_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 12(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_14):
+ cmp $80, %ecx
+ lea -48(%ecx), %ecx
+ mov %edx, %eax
+ jae L(shr_14_gobble)
+
+ movdqa 16(%esi), %xmm1
+ movdqa %xmm1, %xmm2
+ palignr $14, (%esi), %xmm1
+ pcmpeqb (%edi), %xmm1
+
+ movdqa 32(%esi), %xmm3
+ palignr $14, %xmm2, %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+ pand %xmm1, %xmm3
+ pmovmskb %xmm3, %edx
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+ lea (%ecx, %edi,1), %eax
+ lea 14(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(shr_14_gobble):
+ sub $32, %ecx
+ movdqa 16(%esi), %xmm0
+ palignr $14, (%esi), %xmm0
+ pcmpeqb (%edi), %xmm0
+
+ movdqa 32(%esi), %xmm3
+ palignr $14, 16(%esi), %xmm3
+ pcmpeqb 16(%edi), %xmm3
+
+L(shr_14_gobble_loop):
+ pand %xmm0, %xmm3
+ sub $32, %ecx
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+
+ movdqa 64(%esi), %xmm3
+ palignr $14,48(%esi), %xmm3
+ sbb $0xffff, %edx
+ movdqa 48(%esi), %xmm0
+ palignr $14,32(%esi), %xmm0
+ pcmpeqb 32(%edi), %xmm0
+ lea 32(%esi), %esi
+ pcmpeqb 48(%edi), %xmm3
+
+ lea 32(%edi), %edi
+ jz L(shr_14_gobble_loop)
+ pand %xmm0, %xmm3
+
+ cmp $0, %ecx
+ jge L(shr_14_gobble_next)
+ inc %edx
+ add $32, %ecx
+L(shr_14_gobble_next):
+ test %edx, %edx
+ jnz L(exit)
+
+ pmovmskb %xmm3, %edx
+ movdqa %xmm0, %xmm1
+ lea 32(%edi), %edi
+ lea 32(%esi), %esi
+ sub $0xffff, %edx
+ jnz L(exit)
+
+ lea (%ecx, %edi,1), %eax
+ lea 14(%ecx, %esi,1), %edx
+ POP (%edi)
+ POP (%esi)
+ jmp L(less48bytes)
+
+ CFI_RESTORE_STATE
+ CFI_REMEMBER_STATE
+ .p2align 4
+L(exit):
+ pmovmskb %xmm1, %ebx
+ sub $0xffff, %ebx
+ jz L(first16bytes)
+ lea -16(%esi), %esi
+ lea -16(%edi), %edi
+ mov %ebx, %edx
+
+L(first16bytes):
+ add %eax, %esi
+L(less16bytes):
+ test %dl, %dl
+ jz L(next_four_words)
+ test $15, %dl
+ jz L(second_two_words)
+ test $3, %dl
+ jz L(second_word)
+ movzwl -16(%edi), %eax
+ movzwl -16(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(second_word):
+ movzwl -14(%edi), %eax
+ movzwl -14(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(second_two_words):
+ test $63, %dl
+ jz L(fourth_word)
+ movzwl -12(%edi), %eax
+ movzwl -12(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(fourth_word):
+ movzwl -10(%edi), %eax
+ movzwl -10(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(next_four_words):
+ test $15, %dh
+ jz L(fourth_two_words)
+ test $3, %dh
+ jz L(sixth_word)
+ movzwl -8(%edi), %eax
+ movzwl -8(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(sixth_word):
+ movzwl -6(%edi), %eax
+ movzwl -6(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(fourth_two_words):
+ test $63, %dh
+ jz L(eighth_word)
+ movzwl -4(%edi), %eax
+ movzwl -4(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+ .p2align 4
+L(eighth_word):
+ movzwl -2(%edi), %eax
+ movzwl -2(%esi), %ebx
+ subl %ebx, %eax
+ RETURN
+
+
+ CFI_PUSH (%ebx)
+
+ .p2align 4
+L(more8bytes):
+ cmp $16, %ecx
+ jae L(more16bytes)
+ cmp $8, %ecx
+ je L(8bytes)
+ cmp $10, %ecx
+ je L(10bytes)
+ cmp $12, %ecx
+ je L(12bytes)
+ jmp L(14bytes)
+
+ .p2align 4
+L(more16bytes):
+ cmp $24, %ecx
+ jae L(more24bytes)
+ cmp $16, %ecx
+ je L(16bytes)
+ cmp $18, %ecx
+ je L(18bytes)
+ cmp $20, %ecx
+ je L(20bytes)
+ jmp L(22bytes)
+
+ .p2align 4
+L(more24bytes):
+ cmp $32, %ecx
+ jae L(more32bytes)
+ cmp $24, %ecx
+ je L(24bytes)
+ cmp $26, %ecx
+ je L(26bytes)
+ cmp $28, %ecx
+ je L(28bytes)
+ jmp L(30bytes)
+
+ .p2align 4
+L(more32bytes):
+ cmp $40, %ecx
+ jae L(more40bytes)
+ cmp $32, %ecx
+ je L(32bytes)
+ cmp $34, %ecx
+ je L(34bytes)
+ cmp $36, %ecx
+ je L(36bytes)
+ jmp L(38bytes)
+
+ .p2align 4
+L(less48bytes):
+ cmp $8, %ecx
+ jae L(more8bytes)
+ cmp $2, %ecx
+ je L(2bytes)
+ cmp $4, %ecx
+ je L(4bytes)
+ jmp L(6bytes)
+
+ .p2align 4
+L(more40bytes):
+ cmp $40, %ecx
+ je L(40bytes)
+ cmp $42, %ecx
+ je L(42bytes)
+ cmp $44, %ecx
+ je L(44bytes)
+ jmp L(46bytes)
+
+ .p2align 4
+L(46bytes):
+ movzwl -46(%eax), %ecx
+ movzwl -46(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(44bytes):
+ movzwl -44(%eax), %ecx
+ movzwl -44(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(42bytes):
+ movzwl -42(%eax), %ecx
+ movzwl -42(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(40bytes):
+ movzwl -40(%eax), %ecx
+ movzwl -40(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(38bytes):
+ movzwl -38(%eax), %ecx
+ movzwl -38(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(36bytes):
+ movzwl -36(%eax), %ecx
+ movzwl -36(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(34bytes):
+ movzwl -34(%eax), %ecx
+ movzwl -34(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(32bytes):
+ movzwl -32(%eax), %ecx
+ movzwl -32(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(30bytes):
+ movzwl -30(%eax), %ecx
+ movzwl -30(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(28bytes):
+ movzwl -28(%eax), %ecx
+ movzwl -28(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(26bytes):
+ movzwl -26(%eax), %ecx
+ movzwl -26(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(24bytes):
+ movzwl -24(%eax), %ecx
+ movzwl -24(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(22bytes):
+ movzwl -22(%eax), %ecx
+ movzwl -22(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(20bytes):
+ movzwl -20(%eax), %ecx
+ movzwl -20(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(18bytes):
+ movzwl -18(%eax), %ecx
+ movzwl -18(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(16bytes):
+ movzwl -16(%eax), %ecx
+ movzwl -16(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(14bytes):
+ movzwl -14(%eax), %ecx
+ movzwl -14(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(12bytes):
+ movzwl -12(%eax), %ecx
+ movzwl -12(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(10bytes):
+ movzwl -10(%eax), %ecx
+ movzwl -10(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(8bytes):
+ movzwl -8(%eax), %ecx
+ movzwl -8(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(6bytes):
+ movzwl -6(%eax), %ecx
+ movzwl -6(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(4bytes):
+ movzwl -4(%eax), %ecx
+ movzwl -4(%edx), %ebx
+ subl %ebx, %ecx
+ jne L(memcmp16_exit)
+L(2bytes):
+ movzwl -2(%eax), %eax
+ movzwl -2(%edx), %ebx
+ subl %ebx, %eax
+ POP (%ebx)
+ ret
+ CFI_PUSH (%ebx)
+
+ .p2align 4
+L(memcmp16_exit):
+ POP (%ebx)
+ mov %ecx, %eax
+ ret
+END_FUNCTION MEMCMP
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 68f46ad26c..15221294a8 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1277,14 +1277,12 @@ DEFINE_FUNCTION art_quick_instrumentation_exit
mov %esp, %ecx // Remember SP
subl LITERAL(8), %esp // Save float return value.
CFI_ADJUST_CFA_OFFSET(8)
- movd %xmm0, (%esp)
+ movq %xmm0, (%esp)
PUSH edx // Save gpr return value.
PUSH eax
- subl LITERAL(8), %esp // Align stack
- movd %xmm0, (%esp)
- subl LITERAL(8), %esp // Pass float return value.
- CFI_ADJUST_CFA_OFFSET(8)
- movd %xmm0, (%esp)
+ subl LITERAL(16), %esp // Align stack
+ CFI_ADJUST_CFA_OFFSET(16)
+ movq %xmm0, (%esp) // Pass float return value.
PUSH edx // Pass gpr return value.
PUSH eax
PUSH ecx // Pass SP.
@@ -1299,7 +1297,7 @@ DEFINE_FUNCTION art_quick_instrumentation_exit
// (ebx is pretending to be our LR).
POP eax // Restore gpr return value.
POP edx
- movd (%esp), %xmm0 // Restore fpr return value.
+ movq (%esp), %xmm0 // Restore fpr return value.
addl LITERAL(8), %esp
CFI_ADJUST_CFA_OFFSET(-8)
RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 50b2de4a4f..48bc240dab 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1495,7 +1495,7 @@ DEFINE_FUNCTION art_quick_instrumentation_exit
PUSH rax // Save integer result.
subq LITERAL(8), %rsp // Save floating-point result.
CFI_ADJUST_CFA_OFFSET(8)
- movd %xmm0, (%rsp)
+ movq %xmm0, (%rsp)
movq %gs:THREAD_SELF_OFFSET, %rdi // Pass Thread.
movq %rax, %rdx // Pass integer result.
@@ -1506,7 +1506,7 @@ DEFINE_FUNCTION art_quick_instrumentation_exit
movq %rax, %rdi // Store return PC
movq %rdx, %rsi // Store second return PC in hidden arg.
- movd (%rsp), %xmm0 // Restore floating-point result.
+ movq (%rsp), %xmm0 // Restore floating-point result.
addq LITERAL(8), %rsp
CFI_ADJUST_CFA_OFFSET(-8)
POP rax // Restore integer result.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 7779547725..c0a865fd0b 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -41,6 +41,7 @@ Mutex* Locks::modify_ldt_lock_ = nullptr;
ReaderWriterMutex* Locks::mutator_lock_ = nullptr;
Mutex* Locks::runtime_shutdown_lock_ = nullptr;
Mutex* Locks::thread_list_lock_ = nullptr;
+Mutex* Locks::thread_list_suspend_thread_lock_ = nullptr;
Mutex* Locks::thread_suspend_count_lock_ = nullptr;
Mutex* Locks::trace_lock_ = nullptr;
Mutex* Locks::profiler_lock_ = nullptr;
@@ -149,7 +150,8 @@ void BaseMutex::CheckSafeToWait(Thread* self) {
for (int i = kLockLevelCount - 1; i >= 0; --i) {
if (i != level_) {
BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
- if (held_mutex != NULL) {
+ // We expect waits to happen while holding the thread list suspend thread lock.
+ if (held_mutex != NULL && i != kThreadListSuspendThreadLock) {
LOG(ERROR) << "Holding \"" << held_mutex->name_ << "\" "
<< "(level " << LockLevel(i) << ") while performing wait on "
<< "\"" << name_ << "\" (level " << level_ << ")";
@@ -161,16 +163,10 @@ void BaseMutex::CheckSafeToWait(Thread* self) {
}
}
-inline void BaseMutex::ContentionLogData::AddToWaitTime(uint64_t value) {
+void BaseMutex::ContentionLogData::AddToWaitTime(uint64_t value) {
if (kLogLockContentions) {
// Atomically add value to wait_time.
- uint64_t new_val, old_val;
- volatile int64_t* addr = reinterpret_cast<volatile int64_t*>(&wait_time);
- volatile const int64_t* caddr = const_cast<volatile const int64_t*>(addr);
- do {
- old_val = static_cast<uint64_t>(QuasiAtomic::Read64(caddr));
- new_val = old_val + value;
- } while (!QuasiAtomic::Cas64(static_cast<int64_t>(old_val), static_cast<int64_t>(new_val), addr));
+ wait_time.FetchAndAddSequentiallyConsistent(value);
}
}
@@ -204,7 +200,7 @@ void BaseMutex::DumpContention(std::ostream& os) const {
if (kLogLockContentions) {
const ContentionLogData* data = contention_log_data_;
const ContentionLogEntry* log = data->contention_log;
- uint64_t wait_time = data->wait_time;
+ uint64_t wait_time = data->wait_time.LoadRelaxed();
uint32_t contention_count = data->contention_count.LoadRelaxed();
if (contention_count == 0) {
os << "never contended";
@@ -841,6 +837,7 @@ void Locks::Init() {
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
DCHECK(thread_list_lock_ != nullptr);
+ DCHECK(thread_list_suspend_thread_lock_ != nullptr);
DCHECK(thread_suspend_count_lock_ != nullptr);
DCHECK(trace_lock_ != nullptr);
DCHECK(profiler_lock_ != nullptr);
@@ -848,13 +845,18 @@ void Locks::Init() {
DCHECK(intern_table_lock_ != nullptr);
} else {
// Create global locks in level order from highest lock level to lowest.
- LockLevel current_lock_level = kMutatorLock;
- DCHECK(mutator_lock_ == nullptr);
- mutator_lock_ = new ReaderWriterMutex("mutator lock", current_lock_level);
+ LockLevel current_lock_level = kThreadListSuspendThreadLock;
+ DCHECK(thread_list_suspend_thread_lock_ == nullptr);
+ thread_list_suspend_thread_lock_ =
+ new Mutex("thread list suspend thread by .. lock", current_lock_level);
#define UPDATE_CURRENT_LOCK_LEVEL(new_level) \
- DCHECK_LT(new_level, current_lock_level); \
- current_lock_level = new_level;
+ DCHECK_LT(new_level, current_lock_level); \
+ current_lock_level = new_level;
+
+ UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock);
+ DCHECK(mutator_lock_ == nullptr);
+ mutator_lock_ = new ReaderWriterMutex("mutator lock", current_lock_level);
UPDATE_CURRENT_LOCK_LEVEL(kHeapBitmapLock);
DCHECK(heap_bitmap_lock_ == nullptr);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 8d2cd07aea..818f9d9908 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -93,6 +93,7 @@ enum LockLevel {
kRuntimeShutdownLock,
kHeapBitmapLock,
kMutatorLock,
+ kThreadListSuspendThreadLock,
kZygoteCreationLock,
kLockLevelCount // Must come last.
@@ -160,7 +161,7 @@ class BaseMutex {
// Number of times the Mutex has been contended.
AtomicInteger contention_count;
// Sum of time waited by all contenders in ns.
- volatile uint64_t wait_time;
+ Atomic<uint64_t> wait_time;
void AddToWaitTime(uint64_t value);
ContentionLogData() : wait_time(0) {}
};
@@ -474,6 +475,15 @@ class Locks {
public:
static void Init();
+ // There's a potential race for two threads to try to suspend each other and for both of them
+ // to succeed and get blocked becoming runnable. This lock ensures that only one thread is
+ // requesting suspension of another at any time. As the the thread list suspend thread logic
+ // transitions to runnable, if the current thread were tried to be suspended then this thread
+ // would block holding this lock until it could safely request thread suspension of the other
+ // thread without that thread having a suspension request against this thread. This avoids a
+ // potential deadlock cycle.
+ static Mutex* thread_list_suspend_thread_lock_;
+
// The mutator_lock_ is used to allow mutators to execute in a shared (reader) mode or to block
// mutators by having an exclusive (writer) owner. In normal execution each mutator thread holds
// a share on the mutator_lock_. The garbage collector may also execute with shared access but
@@ -532,7 +542,7 @@ class Locks {
// else | .. running ..
// Goto x | .. running ..
// .. running .. | .. running ..
- static ReaderWriterMutex* mutator_lock_;
+ static ReaderWriterMutex* mutator_lock_ ACQUIRED_AFTER(thread_list_suspend_thread_lock_);
// Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap.
static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_);
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 351de3d48f..bf091d00d2 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -58,6 +58,22 @@ bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
}
}
+bool ScopedFlock::Init(File* file, std::string* error_msg) {
+ file_.reset(new File(dup(file->Fd())));
+ if (file_->Fd() == -1) {
+ file_.reset();
+ *error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
+ file->GetPath().c_str(), strerror(errno));
+ return false;
+ }
+ if (0 != TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX))) {
+ file_.reset();
+ *error_msg = StringPrintf("Failed to lock file '%s': %s", file->GetPath().c_str(), strerror(errno));
+ return false;
+ }
+ return true;
+}
+
File* ScopedFlock::GetFile() {
CHECK(file_.get() != NULL);
return file_.get();
diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h
index f8ed805bea..08612e3016 100644
--- a/runtime/base/scoped_flock.h
+++ b/runtime/base/scoped_flock.h
@@ -37,6 +37,10 @@ class ScopedFlock {
// changed (usually due to a new file being created at the same path)
// between attempts to lock it.
bool Init(const char* filename, std::string* error_msg);
+ // Attempt to acquire an exclusive file lock (see flock(2)) on 'file'.
+ // Returns true if the lock could be acquired or false if an error
+ // occured.
+ bool Init(File* file, std::string* error_msg);
// Returns the (locked) file associated with this instance.
File* GetFile();
@@ -45,6 +49,7 @@ class ScopedFlock {
bool HasFile();
~ScopedFlock();
+
private:
std::unique_ptr<File> file_;
DISALLOW_COPY_AND_ASSIGN(ScopedFlock);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2c11f8b89c..7f89156d7a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -814,6 +814,7 @@ bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_
return false;
}
+ // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
oat_location, &error_msg));
@@ -833,8 +834,6 @@ bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_
// There's no point in going forward and eventually try to regenerate the
// file if we couldn't remove the obsolete one. Mostly likely we will fail
// with the same error when trying to write the new file.
- // In case the clean up failure is due to permission issues it's *mandatory*
- // to stop to avoid regenerating under the wrong user.
// TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
if (obsolete_file_cleanup_failed) {
return false;
@@ -940,6 +939,13 @@ const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_l
actual_image_oat_offset);
return nullptr;
}
+ int32_t expected_patch_delta = image_header.GetPatchDelta();
+ int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+ if (expected_patch_delta != actual_patch_delta) {
+ *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
+ " found %d", oat_location, expected_patch_delta, actual_patch_delta);
+ return nullptr;
+ }
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
if (oat_dex_file == nullptr) {
@@ -1000,19 +1006,23 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
// image header from the image for the right instruction set.
uint32_t image_oat_checksum = 0;
uintptr_t image_oat_data_begin = 0;
- if (instruction_set == kRuntimeISA) {
+ int32_t image_patch_delta = 0;
+ if (instruction_set == Runtime::Current()->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
image_oat_checksum = image_header.GetOatChecksum();
image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
+ image_patch_delta = image_header.GetPatchDelta();
} else {
std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
image_space->GetImageLocation().c_str(), instruction_set));
image_oat_checksum = image_header->GetOatChecksum();
image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
+ image_patch_delta = image_header->GetPatchDelta();
}
const OatHeader& oat_header = oat_file->GetOatHeader();
bool image_check = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
- && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+ && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin)
+ && (oat_header.GetImagePatchDelta() == image_patch_delta));
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
@@ -1051,16 +1061,11 @@ bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
return false;
}
-const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_file_location,
- const char* dex_location,
- std::string* error_msg,
- bool* open_failed) {
- std::unique_ptr<const OatFile> oat_file(FindOatFileFromOatLocation(oat_file_location, error_msg));
- if (oat_file.get() == nullptr) {
- *open_failed = true;
- return nullptr;
- }
- *open_failed = false;
+bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
+ const char* dex_location,
+ std::string* error_msg) {
+ CHECK(oat_file != nullptr);
+ CHECK(dex_location != nullptr);
std::unique_ptr<const DexFile> dex_file;
uint32_t dex_location_checksum;
if (!DexFile::GetChecksum(dex_location, &dex_location_checksum, error_msg)) {
@@ -1070,26 +1075,21 @@ const OatFile* ClassLinker::LoadOatFileAndVerifyDexFile(const std::string& oat_f
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, NULL);
if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
- "dex file '%s': %s", oat_file_location.c_str(), dex_location,
+ "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg->c_str());
- return nullptr;
+ return false;
}
dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
} else {
- bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum,
+ bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
- return nullptr;
+ return false;
}
dex_file.reset(oat_file->GetOatDexFile(dex_location,
&dex_location_checksum)->OpenDexFile(error_msg));
}
-
- if (dex_file.get() != nullptr) {
- return oat_file.release();
- } else {
- return nullptr;
- }
+ return dex_file.get() != nullptr;
}
const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
@@ -1099,51 +1099,25 @@ const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
std::vector<std::string>* error_msgs,
bool* obsolete_file_cleanup_failed) {
*obsolete_file_cleanup_failed = false;
- // Look for an existing file next to dex. for example, for
- // /foo/bar/baz.jar, look for /foo/bar/<isa>/baz.odex.
- std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
- bool open_failed;
+ bool already_opened = false;
+ std::string dex_location_str(dex_location);
+ std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
+ &already_opened,
+ obsolete_file_cleanup_failed,
+ error_msgs));
std::string error_msg;
- const OatFile* oat_file = LoadOatFileAndVerifyDexFile(odex_filename, dex_location, &error_msg,
- &open_failed);
- if (oat_file != nullptr) {
- return oat_file;
- }
- if (dex_location_checksum == nullptr) {
- error_msgs->push_back(StringPrintf("Failed to open oat file from %s and no classes.dex found in"
- "%s: %s", odex_filename.c_str(), dex_location,
+ if (oat_file.get() == nullptr) {
+ error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
+ dex_location));
+ return nullptr;
+ } else if (!VerifyOatWithDexFile(oat_file.get(), dex_location, &error_msg)) {
+ error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
+ "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg.c_str()));
return nullptr;
+ } else {
+ return oat_file.release();
}
-
- std::string cache_error_msg;
- const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
- std::string cache_location(GetDalvikCacheFilenameOrDie(dex_location,
- dalvik_cache.c_str()));
- oat_file = LoadOatFileAndVerifyDexFile(cache_location, dex_location, &cache_error_msg,
- &open_failed);
- if (oat_file != nullptr) {
- return oat_file;
- }
-
- if (!open_failed && TEMP_FAILURE_RETRY(unlink(cache_location.c_str())) != 0) {
- std::string error_msg = StringPrintf("Failed to remove obsolete file from %s when searching"
- "for dex file %s: %s",
- cache_location.c_str(), dex_location, strerror(errno));
- error_msgs->push_back(error_msg);
- VLOG(class_linker) << error_msg;
- // Let the caller know that we couldn't remove the obsolete file.
- // This is a good indication that further writes may fail as well.
- *obsolete_file_cleanup_failed = true;
- }
-
- std::string compound_msg = StringPrintf("Failed to open oat file from %s (error '%s') or %s "
- "(error '%s').", odex_filename.c_str(), error_msg.c_str(),
- cache_location.c_str(), cache_error_msg.c_str());
- VLOG(class_linker) << compound_msg;
- error_msgs->push_back(compound_msg);
-
- return nullptr;
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
@@ -1158,6 +1132,277 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string&
return nullptr;
}
+const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
+ InstructionSet isa,
+ bool *already_opened,
+ bool *obsolete_file_cleanup_failed,
+ std::vector<std::string>* error_msgs) {
+ // Find out if we've already opened the file
+ const OatFile* ret = nullptr;
+ std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
+ ret = FindOpenedOatFileFromOatLocation(odex_filename);
+ if (ret != nullptr) {
+ *already_opened = true;
+ return ret;
+ }
+
+ std::string dalvik_cache;
+ bool have_android_data = false;
+ bool have_dalvik_cache = false;
+ GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
+ &have_android_data, &have_dalvik_cache);
+ std::string cache_filename;
+ if (have_dalvik_cache) {
+ cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
+ ret = FindOpenedOatFileFromOatLocation(cache_filename);
+ if (ret != nullptr) {
+ *already_opened = true;
+ return ret;
+ }
+ } else {
+ // If we need to relocate we should just place odex back where it started.
+ cache_filename = odex_filename;
+ }
+
+ ret = nullptr;
+
+ // We know that neither the odex nor the cache'd version is already in use, if it even exists.
+ //
+ // Now we do the following:
+ // 1) Try and open the odex version
+ // 2) If present, checksum-verified & relocated correctly return it
+ // 3) Close the odex version to free up its address space.
+ // 4) Try and open the cache version
+ // 5) If present, checksum-verified & relocated correctly return it
+ // 6) Close the cache version to free up its address space.
+ // 7) If we should relocate:
+ // a) If we have opened and checksum-verified the odex version relocate it to
+ // 'cache_filename' and return it
+ // b) If we have opened and checksum-verified the cache version relocate it in place and return
+ // it. This should not happen often (I think only the run-test's will hit this case).
+ // 8) If the cache-version was present we should delete it since it must be obsolete if we get to
+ // this point.
+ // 9) Return nullptr
+
+ *already_opened = false;
+ const Runtime* runtime = Runtime::Current();
+ CHECK(runtime != nullptr);
+ bool executable = !runtime->IsCompiler();
+
+ std::string odex_error_msg;
+ bool should_patch_system = false;
+ bool odex_checksum_verified = false;
+ {
+ // There is a high probability that these both these oat files map similar/the same address
+ // spaces so we must scope them like this so they each gets its turn.
+ std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, NULL,
+ executable, &odex_error_msg));
+ if (odex_oat_file.get() != nullptr && CheckOatFile(odex_oat_file.get(), isa,
+ &odex_checksum_verified,
+ &odex_error_msg)) {
+ error_msgs->push_back(odex_error_msg);
+ return odex_oat_file.release();
+ } else if (odex_checksum_verified) {
+ // We can just relocate
+ should_patch_system = true;
+ odex_error_msg = "Image Patches are incorrect";
+ }
+ }
+
+
+ std::string cache_error_msg;
+ bool should_patch_cache = false;
+ bool cache_checksum_verified = false;
+ if (have_dalvik_cache) {
+ std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, NULL,
+ executable, &cache_error_msg));
+ if (cache_oat_file.get() != nullptr && CheckOatFile(cache_oat_file.get(), isa,
+ &cache_checksum_verified,
+ &cache_error_msg)) {
+ error_msgs->push_back(cache_error_msg);
+ return cache_oat_file.release();
+ } else if (cache_checksum_verified) {
+ // We can just relocate
+ should_patch_cache = true;
+ cache_error_msg = "Image Patches are incorrect";
+ }
+ } else if (have_android_data) {
+ // dalvik_cache does not exist but android data does. This means we should be able to create
+ // it, so we should try.
+ GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
+ }
+
+ ret = nullptr;
+ std::string error_msg;
+ if (runtime->CanRelocate()) {
+ // Run relocation
+ const std::string& image_location =
+ Runtime::Current()->GetHeap()->GetImageSpace()->GetImageLocation();
+ if (odex_checksum_verified && should_patch_system) {
+ ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
+ } else if (cache_checksum_verified && should_patch_cache) {
+ CHECK(have_dalvik_cache);
+ ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
+ }
+ }
+ if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
+ // implicitly: were able to fine where the cached version is but we were unable to use it,
+ // either as a destination for relocation or to open a file. We should delete it if it is
+ // there.
+ if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
+ std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
+ "searching for dex file %s: %s",
+ cache_filename.c_str(), dex_location.c_str(),
+ strerror(errno));
+ error_msgs->push_back(rm_error_msg);
+ VLOG(class_linker) << rm_error_msg;
+ // Let the caller know that we couldn't remove the obsolete file.
+ // This is a good indication that further writes may fail as well.
+ *obsolete_file_cleanup_failed = true;
+ }
+ }
+ if (ret == nullptr) {
+ VLOG(class_linker) << error_msg;
+ error_msgs->push_back(error_msg);
+ std::string relocation_msg;
+ if (runtime->CanRelocate()) {
+ relocation_msg = StringPrintf(" and relocation failed");
+ }
+ if (have_dalvik_cache && cache_checksum_verified) {
+ error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
+ "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
+ cache_filename.c_str(), cache_error_msg.c_str(),
+ relocation_msg.c_str());
+ } else {
+ error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
+ "dalvik_cache availible)%s.", odex_filename.c_str(),
+ odex_error_msg.c_str(), relocation_msg.c_str());
+ }
+ VLOG(class_linker) << error_msg;
+ error_msgs->push_back(error_msg);
+ }
+ return ret;
+}
+
+const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
+ const std::string& output_oat,
+ const std::string& image_location,
+ InstructionSet isa,
+ std::string* error_msg) {
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
+ std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
+
+ std::string isa_arg("--instruction-set=");
+ isa_arg += GetInstructionSetString(isa);
+ std::string input_oat_filename_arg("--input-oat-file=");
+ input_oat_filename_arg += input_oat;
+ std::string output_oat_filename_arg("--output-oat-file=");
+ output_oat_filename_arg += output_oat;
+ std::string patched_image_arg("--patched-image-location=");
+ patched_image_arg += image_location;
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+ argv.push_back(isa_arg);
+ argv.push_back(input_oat_filename_arg);
+ argv.push_back(output_oat_filename_arg);
+ argv.push_back(patched_image_arg);
+
+ std::string command_line(Join(argv, ' '));
+ LOG(INFO) << "Relocate Oat File: " << command_line;
+ bool success = Exec(argv, error_msg);
+ if (success) {
+ std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, NULL,
+ !Runtime::Current()->IsCompiler(), error_msg));
+ bool checksum_verified = false;
+ if (output.get() != nullptr && CheckOatFile(output.get(), isa, &checksum_verified, error_msg)) {
+ return output.release();
+ } else if (output.get() != nullptr) {
+ *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
+ "but output file '%s' failed verifcation: %s",
+ input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
+ } else {
+ *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
+ "but was unable to open output file '%s': %s",
+ input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
+ }
+ } else {
+ *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
+ "failed: %s", input_oat.c_str(), output_oat.c_str(),
+ error_msg->c_str());
+ }
+ return nullptr;
+}
+
+int32_t ClassLinker::GetRequiredDelta(const OatFile* oat_file, InstructionSet isa) {
+ Runtime* runtime = Runtime::Current();
+ int32_t real_patch_delta;
+ const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (isa == Runtime::Current()->GetInstructionSet()) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ real_patch_delta = image_header.GetPatchDelta();
+ } else {
+ std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+ image_space->GetImageLocation().c_str(), isa));
+ real_patch_delta = image_header->GetPatchDelta();
+ }
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ return real_patch_delta - oat_header.GetImagePatchDelta();
+}
+
+bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+ bool* checksum_verified,
+ std::string* error_msg) {
+ std::string compound_msg("Oat file failed to verify: ");
+ Runtime* runtime = Runtime::Current();
+ uint32_t real_image_checksum;
+ void* real_image_oat_offset;
+ int32_t real_patch_delta;
+ const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+ if (isa == Runtime::Current()->GetInstructionSet()) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ real_image_checksum = image_header.GetOatChecksum();
+ real_image_oat_offset = image_header.GetOatDataBegin();
+ real_patch_delta = image_header.GetPatchDelta();
+ } else {
+ std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+ image_space->GetImageLocation().c_str(), isa));
+ real_image_checksum = image_header->GetOatChecksum();
+ real_image_oat_offset = image_header->GetOatDataBegin();
+ real_patch_delta = image_header->GetPatchDelta();
+ }
+
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+
+ uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
+ *checksum_verified = oat_image_checksum == real_image_checksum;
+ if (!*checksum_verified) {
+ compound_msg += StringPrintf(" Oat Image Checksum Incorrect (expected 0x%x, recieved 0x%x)",
+ real_image_checksum, oat_image_checksum);
+ }
+
+ void* oat_image_oat_offset =
+ reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
+ bool offset_verified = oat_image_oat_offset == real_image_oat_offset;
+ if (!offset_verified) {
+ compound_msg += StringPrintf(" Oat Image oat offset incorrect (expected 0x%p, recieved 0x%p)",
+ real_image_oat_offset, oat_image_oat_offset);
+ }
+
+ int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+ bool patch_delta_verified = oat_patch_delta == real_patch_delta;
+ if (!patch_delta_verified) {
+ compound_msg += StringPrintf(" Oat image patch delta incorrect (expected 0x%x, recieved 0x%x)",
+ real_patch_delta, oat_patch_delta);
+ }
+
+ bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
+ if (ret) {
+ *error_msg = compound_msg;
+ }
+ return ret;
+}
+
const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
std::string* error_msg) {
const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
@@ -1660,23 +1905,26 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor,
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
- if (UNLIKELY(klass.Get() == NULL)) {
+ if (UNLIKELY(klass.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
- return NULL;
+ return nullptr;
}
klass->SetDexCache(FindDexCache(dex_file));
LoadClass(dex_file, dex_class_def, klass, class_loader.Get());
- // Check for a pending exception during load
+ ObjectLock<mirror::Class> lock(self, klass);
if (self->IsExceptionPending()) {
- klass->SetStatus(mirror::Class::kStatusError, self);
- return NULL;
+ // An exception occured during load, set status to erroneous while holding klass' lock in case
+ // notification is necessary.
+ if (!klass->IsErroneous()) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ }
+ return nullptr;
}
- ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), Hash(descriptor));
- if (existing != NULL) {
+ if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return EnsureResolved(self, descriptor, existing);
@@ -1686,8 +1934,10 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor,
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
- klass->SetStatus(mirror::Class::kStatusError, self);
- return NULL;
+ if (!klass->IsErroneous()) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ }
+ return nullptr;
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
@@ -1698,8 +1948,10 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor,
mirror::Class* new_class = nullptr;
if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
// Linking failed.
- klass->SetStatus(mirror::Class::kStatusError, self);
- return NULL;
+ if (!klass->IsErroneous()) {
+ klass->SetStatus(mirror::Class::kStatusError, self);
+ }
+ return nullptr;
}
CHECK(new_class != nullptr) << descriptor;
CHECK(new_class->IsResolved()) << descriptor;
@@ -2603,8 +2855,9 @@ mirror::Class* ClassLinker::UpdateClass(const char* descriptor, mirror::Class* k
for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash;
++it) {
- mirror::Class* entry = it->second;
- if (entry == existing) {
+ mirror::Class** root = &it->second;
+ mirror::Class* klass = ReadBarrier::BarrierForRoot<mirror::Class, kWithReadBarrier>(root);
+ if (klass == existing) {
class_table_.erase(it);
break;
}
@@ -3570,9 +3823,9 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) {
MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
if (klass->HasSuperClass() &&
klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) {
- for (int i = klass->GetSuperClass()->GetVTable()->GetLength() - 1; i >= 0; --i) {
- mh.ChangeMethod(klass->GetVTable()->GetWithoutChecks(i));
- super_mh.ChangeMethod(klass->GetSuperClass()->GetVTable()->GetWithoutChecks(i));
+ for (int i = klass->GetSuperClass()->GetVTableLength() - 1; i >= 0; --i) {
+ mh.ChangeMethod(klass->GetVTableEntry(i));
+ super_mh.ChangeMethod(klass->GetSuperClass()->GetVTableEntry(i));
if (mh.GetMethod() != super_mh.GetMethod() &&
!mh.HasSameSignatureWithDifferentClassLoaders(&super_mh)) {
ThrowLinkageError(klass.Get(),
@@ -3730,10 +3983,6 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror:
// This will notify waiters on new_class that saw the not yet resolved
// class in the class_table_ during EnsureResolved.
new_class_h->SetStatus(mirror::Class::kStatusResolved, self);
-
- // Only embedded imt should be used from this point.
- new_class_h->SetImTable(NULL);
- // TODO: remove vtable and only use embedded vtable.
}
return true;
}
@@ -3866,17 +4115,31 @@ bool ClassLinker::LinkMethods(Thread* self, Handle<mirror::Class> klass,
bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {
if (klass->HasSuperClass()) {
uint32_t max_count = klass->NumVirtualMethods() +
- klass->GetSuperClass()->GetVTable()->GetLength();
- size_t actual_count = klass->GetSuperClass()->GetVTable()->GetLength();
+ klass->GetSuperClass()->GetVTableLength();
+ size_t actual_count = klass->GetSuperClass()->GetVTableLength();
CHECK_LE(actual_count, max_count);
- // TODO: do not assign to the vtable field until it is fully constructed.
StackHandleScope<3> hs(self);
- Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable(
- hs.NewHandle(klass->GetSuperClass()->GetVTable()->CopyOf(self, max_count)));
- if (UNLIKELY(vtable.Get() == NULL)) {
- CHECK(self->IsExceptionPending()); // OOME.
- return false;
+ Handle<mirror::ObjectArray<mirror::ArtMethod>> vtable;
+ mirror::Class* super_class = klass->GetSuperClass();
+ if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
+ vtable = hs.NewHandle(AllocArtMethodArray(self, max_count));
+ if (UNLIKELY(vtable.Get() == nullptr)) {
+ CHECK(self->IsExceptionPending()); // OOME.
+ return false;
+ }
+ int len = super_class->GetVTableLength();
+ for (int i = 0; i < len; i++) {
+ vtable->Set<false>(i, super_class->GetVTableEntry(i));
+ }
+ } else {
+ CHECK(super_class->GetVTable() != nullptr) << PrettyClass(super_class);
+ vtable = hs.NewHandle(super_class->GetVTable()->CopyOf(self, max_count));
+ if (UNLIKELY(vtable.Get() == nullptr)) {
+ CHECK(self->IsExceptionPending()); // OOME.
+ return false;
+ }
}
+
// See if any of our virtual methods override the superclass.
MethodHelper local_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
MethodHelper super_mh(hs.NewHandle<mirror::ArtMethod>(nullptr));
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c17f88d6d8..b108f61e8b 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -265,10 +265,6 @@ class ClassLinker {
std::string* error_msg)
LOCKS_EXCLUDED(Locks::mutator_lock_);
- const OatFile* FindOatFileFromOatLocation(const std::string& location,
- std::string* error_msg)
- LOCKS_EXCLUDED(dex_lock_);
-
// Find or create the oat file holding dex_location. Then load all corresponding dex files
// (if multidex) into the given vector.
bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
@@ -546,9 +542,31 @@ class ClassLinker {
const OatFile* FindOpenedOatFile(const char* oat_location, const char* dex_location,
const uint32_t* const dex_location_checksum)
LOCKS_EXCLUDED(dex_lock_);
+
+ // Will open the oat file directly without relocating, even if we could/should do relocation.
+ const OatFile* FindOatFileFromOatLocation(const std::string& oat_location,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(dex_lock_);
+
const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
LOCKS_EXCLUDED(dex_lock_);
+ const OatFile* OpenOatFileFromDexLocation(const std::string& dex_location,
+ InstructionSet isa,
+ bool* already_opened,
+ bool* obsolete_file_cleanup_failed,
+ std::vector<std::string>* error_msg)
+ LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
+
+ const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
+ const std::string& image_location, InstructionSet isa,
+ std::string* error_msg)
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
+
+ bool CheckOatFile(const OatFile* oat_file, InstructionSet isa,
+ bool* checksum_verified, std::string* error_msg);
+ int32_t GetRequiredDelta(const OatFile* oat_file, InstructionSet isa);
+
// Note: will not register the oat file.
const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
uint32_t dex_location_checksum,
@@ -575,14 +593,10 @@ class ClassLinker {
bool* obsolete_file_cleanup_failed)
LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
- // Find a verify an oat file with the given dex file. Will return nullptr when the oat file
- // was not found or the dex file could not be verified.
- // Note: Does not register the oat file.
- const OatFile* LoadOatFileAndVerifyDexFile(const std::string& oat_file_location,
- const char* dex_location,
- std::string* error_msg,
- bool* open_failed)
- LOCKS_EXCLUDED(dex_lock_);
+ // verify an oat file with the given dex file. Will return false when the dex file could not be
+ // verified. Will return true otherwise.
+ bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
+ std::string* error_msg);
mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass,
mirror::Class* proxy_class)
@@ -720,6 +734,8 @@ class ClassLinker {
const void* quick_to_interpreter_bridge_trampoline_;
friend class ImageWriter; // for GetClassRoots
+ friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
+ friend class ElfPatcher; // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
FRIEND_TEST(ClassLinkerTest, ClassRootDescriptors);
FRIEND_TEST(mirror::DexCacheTest, Open);
FRIEND_TEST(ExceptionTest, FindExceptionHandler);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 21fe0067ed..8e16d9bd49 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -91,7 +91,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_EQ(0U, primitive->NumInstanceFields());
EXPECT_EQ(0U, primitive->NumStaticFields());
EXPECT_EQ(0U, primitive->NumDirectInterfaces());
- EXPECT_TRUE(primitive->GetVTable() == NULL);
+ EXPECT_FALSE(primitive->HasVTable());
EXPECT_EQ(0, primitive->GetIfTableCount());
EXPECT_TRUE(primitive->GetIfTable() == NULL);
EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags());
@@ -143,7 +143,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_EQ(0U, array->NumInstanceFields());
EXPECT_EQ(0U, array->NumStaticFields());
EXPECT_EQ(2U, array->NumDirectInterfaces());
- EXPECT_TRUE(array->GetVTable() != NULL);
+ EXPECT_TRUE(array->ShouldHaveEmbeddedImtAndVTable());
EXPECT_EQ(2, array->GetIfTableCount());
ASSERT_TRUE(array->GetIfTable() != NULL);
mirror::Class* direct_interface0 = mirror::Class::GetDirectInterface(self, array, 0);
@@ -216,7 +216,7 @@ class ClassLinkerTest : public CommonRuntimeTest {
EXPECT_NE(0U, klass->NumDirectMethods());
}
}
- EXPECT_EQ(klass->IsInterface(), klass->GetVTable() == NULL);
+ EXPECT_EQ(klass->IsInterface(), !klass->HasVTable());
mirror::IfTable* iftable = klass->GetIfTable();
for (int i = 0; i < klass->GetIfTableCount(); i++) {
mirror::Class* interface = iftable->GetInterface(i);
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index f47f13d4b5..2826f890f4 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -199,12 +199,13 @@ void CommonRuntimeTest::SetUp() {
runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test
}
-void CommonRuntimeTest::TearDown() {
- const char* android_data = getenv("ANDROID_DATA");
- ASSERT_TRUE(android_data != nullptr);
- DIR* dir = opendir(dalvik_cache_.c_str());
+
+void CommonRuntimeTest::ClearDirectory(const char* dirpath) {
+ ASSERT_TRUE(dirpath != nullptr);
+ DIR* dir = opendir(dirpath);
ASSERT_TRUE(dir != nullptr);
dirent* e;
+ struct stat s;
while ((e = readdir(dir)) != nullptr) {
if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
continue;
@@ -212,10 +213,24 @@ void CommonRuntimeTest::TearDown() {
std::string filename(dalvik_cache_);
filename.push_back('/');
filename.append(e->d_name);
- int unlink_result = unlink(filename.c_str());
- ASSERT_EQ(0, unlink_result);
+ int stat_result = lstat(filename.c_str(), &s);
+ ASSERT_EQ(0, stat_result) << "unable to stat " << filename;
+ if (S_ISDIR(s.st_mode)) {
+ ClearDirectory(filename.c_str());
+ int rmdir_result = rmdir(filename.c_str());
+ ASSERT_EQ(0, rmdir_result) << filename;
+ } else {
+ int unlink_result = unlink(filename.c_str());
+ ASSERT_EQ(0, unlink_result) << filename;
+ }
}
closedir(dir);
+}
+
+void CommonRuntimeTest::TearDown() {
+ const char* android_data = getenv("ANDROID_DATA");
+ ASSERT_TRUE(android_data != nullptr);
+ ClearDirectory(dalvik_cache_.c_str());
int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
ASSERT_EQ(0, rmdir_cache_result);
int rmdir_data_result = rmdir(android_data_.c_str());
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index d0450317a3..eb963525e8 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -81,6 +81,8 @@ class CommonRuntimeTest : public testing::Test {
// Allow subclases such as CommonCompilerTest to add extra options.
virtual void SetUpRuntimeOptions(RuntimeOptions* options) {}
+ void ClearDirectory(const char* dirpath);
+
virtual void TearDown();
std::string GetLibCoreDexFileName();
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index b07043f5d7..d1a68615b1 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -36,6 +36,10 @@ class CompilerCallbacks {
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
virtual void ClassRejected(ClassReference ref) = 0;
+ // Return true if we should attempt to relocate to a random base address if we have not already
+ // done so. Return false if relocating in this way would be problematic.
+ virtual bool IsRelocationPossible() = 0;
+
protected:
CompilerCallbacks() { }
};
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e4ab9f5f52..5e784b1848 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2250,15 +2250,18 @@ void Dbg::ResumeVM() {
}
JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) {
- ScopedLocalRef<jobject> peer(Thread::Current()->GetJniEnv(), NULL);
+ Thread* self = Thread::Current();
+ ScopedLocalRef<jobject> peer(self->GetJniEnv(), NULL);
{
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
peer.reset(soa.AddLocalReference<jobject>(gRegistry->Get<mirror::Object*>(thread_id)));
}
if (peer.get() == NULL) {
return JDWP::ERR_THREAD_NOT_ALIVE;
}
- // Suspend thread to build stack trace.
+ // Suspend thread to build stack trace. Take suspend thread lock to avoid races with threads
+ // trying to suspend this one.
+ MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_);
bool timed_out;
Thread* thread = ThreadList::SuspendThreadByPeer(peer.get(), request_suspension, true,
&timed_out);
@@ -3032,7 +3035,7 @@ static const Breakpoint* FindFirstBreakpointForMethod(mirror::ArtMethod* m)
// Sanity checks all existing breakpoints on the same method.
static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, bool need_full_deoptimization)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) {
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::breakpoint_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
for (const Breakpoint& breakpoint : gBreakpoints) {
CHECK_EQ(need_full_deoptimization, breakpoint.NeedFullDeoptimization());
@@ -3128,7 +3131,7 @@ class ScopedThreadSuspension {
ScopedThreadSuspension(Thread* self, JDWP::ObjectId thread_id)
LOCKS_EXCLUDED(Locks::thread_list_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
- thread_(NULL),
+ thread_(nullptr),
error_(JDWP::ERR_NONE),
self_suspend_(false),
other_suspend_(false) {
@@ -3144,10 +3147,15 @@ class ScopedThreadSuspension {
soa.Self()->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
jobject thread_peer = gRegistry->GetJObject(thread_id);
bool timed_out;
- Thread* suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true,
- &timed_out);
+ Thread* suspended_thread;
+ {
+ // Take suspend thread lock to avoid races with threads trying to suspend this one.
+ MutexLock mu(soa.Self(), *Locks::thread_list_suspend_thread_lock_);
+ suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true,
+ &timed_out);
+ }
CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kWaitingForDebuggerSuspension);
- if (suspended_thread == NULL) {
+ if (suspended_thread == nullptr) {
// Thread terminated from under us while suspending.
error_ = JDWP::ERR_INVALID_THREAD;
} else {
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 291e2d0756..48dcdca64a 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -170,13 +170,29 @@ bool DexFileVerifier::CheckShortyDescriptorMatch(char shorty_char, const char* d
return true;
}
-bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, const char* label) {
+bool DexFileVerifier::CheckListSize(const void* start, size_t count, size_t elem_size,
+ const char* label) {
+ // Check that size is not 0.
+ CHECK_NE(elem_size, 0U);
+
const byte* range_start = reinterpret_cast<const byte*>(start);
- const byte* range_end = reinterpret_cast<const byte*>(end);
const byte* file_start = reinterpret_cast<const byte*>(begin_);
+
+ // Check for overflow.
+ uintptr_t max = 0 - 1;
+ size_t available_bytes_till_end_of_mem = max - reinterpret_cast<uintptr_t>(start);
+ size_t max_count = available_bytes_till_end_of_mem / elem_size;
+ if (max_count < count) {
+ ErrorStringPrintf("Overflow in range for %s: %zx for %zu@%zu", label,
+ static_cast<size_t>(range_start - file_start),
+ count, elem_size);
+ return false;
+ }
+
+ const byte* range_end = range_start + count * elem_size;
const byte* file_end = file_start + size_;
- if (UNLIKELY((range_start < file_start) || (range_start > file_end) ||
- (range_end < file_start) || (range_end > file_end))) {
+ if (UNLIKELY((range_start < file_start) || (range_end > file_end))) {
+ // Note: these two tests are enough as we make sure above that there's no overflow.
ErrorStringPrintf("Bad range for %s: %zx to %zx", label,
static_cast<size_t>(range_start - file_start),
static_cast<size_t>(range_end - file_start));
@@ -185,12 +201,6 @@ bool DexFileVerifier::CheckPointerRange(const void* start, const void* end, cons
return true;
}
-bool DexFileVerifier::CheckListSize(const void* start, uint32_t count,
- uint32_t element_size, const char* label) {
- const byte* list_start = reinterpret_cast<const byte*>(start);
- return CheckPointerRange(list_start, list_start + (count * element_size), label);
-}
-
bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
if (UNLIKELY(field >= limit)) {
ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
@@ -329,7 +339,7 @@ bool DexFileVerifier::CheckMap() {
uint32_t DexFileVerifier::ReadUnsignedLittleEndian(uint32_t size) {
uint32_t result = 0;
- if (LIKELY(CheckPointerRange(ptr_, ptr_ + size, "encoded_value"))) {
+ if (LIKELY(CheckListSize(ptr_, size, sizeof(byte), "encoded_value"))) {
for (uint32_t i = 0; i < size; i++) {
result |= ((uint32_t) *(ptr_++)) << (i * 8);
}
@@ -447,7 +457,7 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, uint32_t access_fla
bool DexFileVerifier::CheckPadding(size_t offset, uint32_t aligned_offset) {
if (offset < aligned_offset) {
- if (!CheckPointerRange(begin_ + offset, begin_ + aligned_offset, "section")) {
+ if (!CheckListSize(begin_ + offset, aligned_offset - offset, sizeof(byte), "section")) {
return false;
}
while (offset < aligned_offset) {
@@ -463,7 +473,7 @@ bool DexFileVerifier::CheckPadding(size_t offset, uint32_t aligned_offset) {
}
bool DexFileVerifier::CheckEncodedValue() {
- if (!CheckPointerRange(ptr_, ptr_ + 1, "encoded_value header")) {
+ if (!CheckListSize(ptr_, 1, sizeof(byte), "encoded_value header")) {
return false;
}
@@ -656,7 +666,7 @@ bool DexFileVerifier::CheckIntraClassDataItem() {
bool DexFileVerifier::CheckIntraCodeItem() {
const DexFile::CodeItem* code_item = reinterpret_cast<const DexFile::CodeItem*>(ptr_);
- if (!CheckPointerRange(code_item, code_item + 1, "code")) {
+ if (!CheckListSize(code_item, 1, sizeof(DexFile::CodeItem), "code")) {
return false;
}
@@ -945,7 +955,7 @@ bool DexFileVerifier::CheckIntraDebugInfoItem() {
}
bool DexFileVerifier::CheckIntraAnnotationItem() {
- if (!CheckPointerRange(ptr_, ptr_ + 1, "annotation visibility")) {
+ if (!CheckListSize(ptr_, 1, sizeof(byte), "annotation visibility")) {
return false;
}
@@ -970,7 +980,7 @@ bool DexFileVerifier::CheckIntraAnnotationItem() {
bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() {
const DexFile::AnnotationsDirectoryItem* item =
reinterpret_cast<const DexFile::AnnotationsDirectoryItem*>(ptr_);
- if (!CheckPointerRange(item, item + 1, "annotations_directory")) {
+ if (!CheckListSize(item, 1, sizeof(DexFile::AnnotationsDirectoryItem), "annotations_directory")) {
return false;
}
@@ -1064,42 +1074,42 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t count, ui
// Check depending on the section type.
switch (type) {
case DexFile::kDexTypeStringIdItem: {
- if (!CheckPointerRange(ptr_, ptr_ + sizeof(DexFile::StringId), "string_ids")) {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) {
return false;
}
ptr_ += sizeof(DexFile::StringId);
break;
}
case DexFile::kDexTypeTypeIdItem: {
- if (!CheckPointerRange(ptr_, ptr_ + sizeof(DexFile::TypeId), "type_ids")) {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::TypeId), "type_ids")) {
return false;
}
ptr_ += sizeof(DexFile::TypeId);
break;
}
case DexFile::kDexTypeProtoIdItem: {
- if (!CheckPointerRange(ptr_, ptr_ + sizeof(DexFile::ProtoId), "proto_ids")) {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::ProtoId), "proto_ids")) {
return false;
}
ptr_ += sizeof(DexFile::ProtoId);
break;
}
case DexFile::kDexTypeFieldIdItem: {
- if (!CheckPointerRange(ptr_, ptr_ + sizeof(DexFile::FieldId), "field_ids")) {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::FieldId), "field_ids")) {
return false;
}
ptr_ += sizeof(DexFile::FieldId);
break;
}
case DexFile::kDexTypeMethodIdItem: {
- if (!CheckPointerRange(ptr_, ptr_ + sizeof(DexFile::MethodId), "method_ids")) {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodId), "method_ids")) {
return false;
}
ptr_ += sizeof(DexFile::MethodId);
break;
}
case DexFile::kDexTypeClassDefItem: {
- if (!CheckPointerRange(ptr_, ptr_ + sizeof(DexFile::ClassDef), "class_defs")) {
+ if (!CheckListSize(ptr_, 1, sizeof(DexFile::ClassDef), "class_defs")) {
return false;
}
ptr_ += sizeof(DexFile::ClassDef);
@@ -1110,7 +1120,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t count, ui
const DexFile::TypeItem* item = &list->GetTypeItem(0);
uint32_t count = list->Size();
- if (!CheckPointerRange(list, list + 1, "type_list") ||
+ if (!CheckListSize(list, 1, sizeof(DexFile::TypeList), "type_list") ||
!CheckListSize(item, count, sizeof(DexFile::TypeItem), "type_list size")) {
return false;
}
@@ -1123,7 +1133,8 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t count, ui
const DexFile::AnnotationSetRefItem* item = list->list_;
uint32_t count = list->size_;
- if (!CheckPointerRange(list, list + 1, "annotation_set_ref_list") ||
+ if (!CheckListSize(list, 1, sizeof(DexFile::AnnotationSetRefList),
+ "annotation_set_ref_list") ||
!CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem),
"annotation_set_ref_list size")) {
return false;
@@ -1137,7 +1148,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t count, ui
const uint32_t* item = set->entries_;
uint32_t count = set->size_;
- if (!CheckPointerRange(set, set + 1, "annotation_set_item") ||
+ if (!CheckListSize(set, 1, sizeof(DexFile::AnnotationSetItem), "annotation_set_item") ||
!CheckListSize(item, count, sizeof(uint32_t), "annotation_set_item size")) {
return false;
}
@@ -1650,6 +1661,12 @@ bool DexFileVerifier::CheckInterClassDefItem() {
return false;
}
+ // Only allow non-runtime modifiers.
+ if ((item->access_flags_ & ~kAccJavaFlagsMask) != 0) {
+ ErrorStringPrintf("Invalid class flags: '%d'", item->access_flags_);
+ return false;
+ }
+
if (item->interfaces_off_ != 0 &&
!CheckOffsetToTypeMap(item->interfaces_off_, DexFile::kDexTypeTypeList)) {
return false;
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index f845993113..cae10639bf 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -40,8 +40,7 @@ class DexFileVerifier {
bool Verify();
bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
- bool CheckPointerRange(const void* start, const void* end, const char* label);
- bool CheckListSize(const void* start, uint32_t count, uint32_t element_size, const char* label);
+ bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label);
bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
bool CheckHeader();
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 90c8fcf980..cb0be04d5c 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -390,26 +390,26 @@ static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx,
case kDirect:
return resolved_method;
case kVirtual: {
- mirror::ObjectArray<mirror::ArtMethod>* vtable = (*this_object)->GetClass()->GetVTable();
+ mirror::Class* klass = (*this_object)->GetClass();
uint16_t vtable_index = resolved_method->GetMethodIndex();
if (access_check &&
- (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength()))) {
+ (!klass->HasVTable() ||
+ vtable_index >= static_cast<uint32_t>(klass->GetVTableLength()))) {
// Behavior to agree with that of the verifier.
ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
resolved_method->GetName(), resolved_method->GetSignature());
return nullptr; // Failure.
}
- DCHECK(vtable != nullptr);
- return vtable->GetWithoutChecks(vtable_index);
+ DCHECK(klass->HasVTable()) << PrettyClass(klass);
+ return klass->GetVTableEntry(vtable_index);
}
case kSuper: {
mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass();
uint16_t vtable_index = resolved_method->GetMethodIndex();
- mirror::ObjectArray<mirror::ArtMethod>* vtable;
if (access_check) {
// Check existence of super class.
- vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr;
- if (vtable == nullptr || vtable_index >= static_cast<uint32_t>(vtable->GetLength())) {
+ if (super_class == nullptr || !super_class->HasVTable() ||
+ vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) {
// Behavior to agree with that of the verifier.
ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
resolved_method->GetName(), resolved_method->GetSignature());
@@ -418,10 +418,9 @@ static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx,
} else {
// Super class must exist.
DCHECK(super_class != nullptr);
- vtable = super_class->GetVTable();
}
- DCHECK(vtable != nullptr);
- return vtable->GetWithoutChecks(vtable_index);
+ DCHECK(super_class->HasVTable());
+ return super_class->GetVTableEntry(vtable_index);
}
case kInterface: {
uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
@@ -555,11 +554,11 @@ static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx,
} else if (is_direct) {
return resolved_method;
} else if (type == kSuper) {
- return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()->
- Get(resolved_method->GetMethodIndex());
+ return referrer->GetDeclaringClass()->GetSuperClass()
+ ->GetVTableEntry(resolved_method->GetMethodIndex());
} else {
DCHECK(type == kVirtual);
- return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex());
+ return this_object->GetClass()->GetVTableEntry(resolved_method->GetMethodIndex());
}
}
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 0fa0e410bf..c1c7631b35 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -110,8 +110,8 @@ mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror:
void ThrowStackOverflowError(Thread* self) {
if (self->IsHandlingStackOverflow()) {
- LOG(ERROR) << "Recursive stack overflow.";
- // We don't fail here because SetStackEndForStackOverflow will print better diagnostics.
+ LOG(ERROR) << "Recursive stack overflow.";
+ // We don't fail here because SetStackEndForStackOverflow will print better diagnostics.
}
if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
@@ -123,15 +123,90 @@ void ThrowStackOverflowError(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
std::string msg("stack size ");
msg += PrettySize(self->GetStackSize());
- // Use low-level JNI routine and pre-baked error class to avoid class linking operations that
- // would consume more stack.
- int rc = ::art::ThrowNewException(env, WellKnownClasses::java_lang_StackOverflowError,
- msg.c_str(), NULL);
- if (rc != JNI_OK) {
- // TODO: ThrowNewException failed presumably because of an OOME, we continue to throw the OOME
- // or die in the CHECK below. We may want to throw a pre-baked StackOverflowError
- // instead.
- LOG(ERROR) << "Couldn't throw new StackOverflowError because JNI ThrowNew failed.";
+
+ // Avoid running Java code for exception initialization.
+ // TODO: Checks to make this a bit less brittle.
+
+ std::string error_msg;
+
+ // Allocate an uninitialized object.
+ ScopedLocalRef<jobject> exc(env,
+ env->AllocObject(WellKnownClasses::java_lang_StackOverflowError));
+ if (exc.get() != nullptr) {
+ // "Initialize".
+ // StackOverflowError -> VirtualMachineError -> Error -> Throwable -> Object.
+ // Only Throwable has "custom" fields:
+ // String detailMessage.
+ // Throwable cause (= this).
+ // List<Throwable> suppressedExceptions (= Collections.emptyList()).
+ // Object stackState;
+ // StackTraceElement[] stackTrace;
+ // Only Throwable has a non-empty constructor:
+ // this.stackTrace = EmptyArray.STACK_TRACE_ELEMENT;
+ // fillInStackTrace();
+
+ // detailMessage.
+ // TODO: Use String::FromModifiedUTF...?
+ ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg.c_str()));
+ if (s.get() != nullptr) {
+ jfieldID detail_message_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "detailMessage", "Ljava/lang/String;");
+ env->SetObjectField(exc.get(), detail_message_id, s.get());
+
+ // cause.
+ jfieldID cause_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "cause", "Ljava/lang/Throwable;");
+ env->SetObjectField(exc.get(), cause_id, exc.get());
+
+ // suppressedExceptions.
+ jfieldID emptylist_id = env->GetStaticFieldID(WellKnownClasses::java_util_Collections,
+ "EMPTY_LIST", "Ljava/util/List;");
+ ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField(
+ WellKnownClasses::java_util_Collections, emptylist_id));
+ CHECK(emptylist.get() != nullptr);
+ jfieldID suppressed_id = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "suppressedExceptions", "Ljava/util/List;");
+ env->SetObjectField(exc.get(), suppressed_id, emptylist.get());
+
+ // stackState is set as result of fillInStackTrace. fillInStackTrace calls
+ // nativeFillInStackTrace.
+ ScopedLocalRef<jobject> stack_state_val(env, nullptr);
+ {
+ ScopedObjectAccessUnchecked soa(env);
+ stack_state_val.reset(soa.Self()->CreateInternalStackTrace<false>(soa));
+ }
+ if (stack_state_val.get() != nullptr) {
+ jfieldID stackstateID = env->GetFieldID(WellKnownClasses::java_lang_Throwable,
+ "stackState", "Ljava/lang/Object;");
+ env->SetObjectField(exc.get(), stackstateID, stack_state_val.get());
+
+ // stackTrace.
+ jfieldID stack_trace_elem_id = env->GetStaticFieldID(
+ WellKnownClasses::libcore_util_EmptyArray, "STACK_TRACE_ELEMENT",
+ "[Ljava/lang/StackTraceElement;");
+ ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
+ WellKnownClasses::libcore_util_EmptyArray, stack_trace_elem_id));
+ jfieldID stacktrace_id = env->GetFieldID(
+ WellKnownClasses::java_lang_Throwable, "stackTrace", "[Ljava/lang/StackTraceElement;");
+ env->SetObjectField(exc.get(), stacktrace_id, stack_trace_elem.get());
+
+ // Throw the exception.
+ ThrowLocation throw_location = self->GetCurrentLocationForThrow();
+ self->SetException(throw_location,
+ reinterpret_cast<mirror::Throwable*>(self->DecodeJObject(exc.get())));
+ } else {
+ error_msg = "Could not create stack trace.";
+ }
+ } else {
+ // Could not allocate a string object.
+ error_msg = "Couldn't throw new StackOverflowError because JNI NewStringUTF failed.";
+ }
+ } else {
+ error_msg = "Could not allocate StackOverflowError object.";
+ }
+
+ if (!error_msg.empty()) {
+ LOG(ERROR) << error_msg;
CHECK(self->IsExceptionPending());
}
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index c5d67aa1df..44c89adada 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -40,7 +40,6 @@ namespace mirror {
class ScopedObjectAccessAlreadyRunnable;
class Thread;
-// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <const bool kAccessCheck>
ALWAYS_INLINE static inline mirror::Class* CheckObjectAlloc(uint32_t type_idx,
mirror::ArtMethod* method,
@@ -56,7 +55,6 @@ ALWAYS_INLINE static inline mirror::Class* CheckClassInitializedForObjectAlloc(m
// cannot be resolved, throw an error. If it can, use it to create an instance.
// When verification/compiler hasn't been able to verify access, optionally perform an access
// check.
-// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
mirror::ArtMethod* method,
@@ -65,7 +63,6 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCode(uint32_t type_id
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Given the context of a calling Method and a resolved class, create an instance.
-// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
template <bool kInstrumented>
ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass,
mirror::ArtMethod* method,
@@ -74,7 +71,6 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeResolved(mirror::
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Given the context of a calling Method and an initialized class, create an instance.
-// TODO: Fix NO_THREAD_SAFETY_ANALYSIS when GCC is smarter.
template <bool kInstrumented>
ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeInitialized(mirror::Class* klass,
mirror::ArtMethod* method,
@@ -83,7 +79,6 @@ ALWAYS_INLINE static inline mirror::Object* AllocObjectFromCodeInitialized(mirro
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <bool kAccessCheck>
ALWAYS_INLINE static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
mirror::ArtMethod* method,
@@ -95,7 +90,6 @@ ALWAYS_INLINE static inline mirror::Class* CheckArrayAlloc(uint32_t type_idx,
// it cannot be resolved, throw an error. If it can, use it to create an array.
// When verification/compiler hasn't been able to verify access, optionally perform an access
// check.
-// TODO: Fix no thread safety analysis when GCC can handle template specialization.
template <bool kAccessCheck, bool kInstrumented>
ALWAYS_INLINE static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
mirror::ArtMethod* method,
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index c7c567f2bd..8fb33cec2f 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -69,9 +69,9 @@ void SemiSpace::BindBitmaps() {
} else if (space->GetLiveBitmap() != nullptr) {
if (space == to_space_ || collect_from_space_only_) {
if (collect_from_space_only_) {
- // Bind the main free list space and the non-moving space to the immune space if a bump
- // pointer space only collection.
- CHECK(space == to_space_ || space == GetHeap()->GetPrimaryFreeListSpace() ||
+ // Bind the bitmaps of the main free list space and the non-moving space we are doing a
+ // bump pointer space only collection.
+ CHECK(space == GetHeap()->GetPrimaryFreeListSpace() ||
space == GetHeap()->GetNonMovingSpace());
}
CHECK(space->IsContinuousMemMapAllocSpace());
@@ -222,7 +222,6 @@ void SemiSpace::MarkingPhase() {
heap_->GetCardTable()->ClearCardTable();
// Need to do this before the checkpoint since we don't want any threads to add references to
// the live stack during the recursive mark.
- t.NewTiming("SwapStacks");
if (kUseThreadLocalAllocationStack) {
TimingLogger::ScopedTiming t("RevokeAllThreadLocalAllocationStacks", GetTimings());
heap_->RevokeAllThreadLocalAllocationStacks(self_);
@@ -492,7 +491,7 @@ mirror::Object* SemiSpace::MarkNonForwardedObject(mirror::Object* obj) {
// If out of space, fall back to the to-space.
forward_address = to_space_->AllocThreadUnsafe(self_, object_size, &bytes_allocated, nullptr);
// No logic for marking the bitmap, so it must be null.
- DCHECK(to_space_->GetLiveBitmap() == nullptr);
+ DCHECK(to_space_live_bitmap_ == nullptr);
} else {
bytes_promoted_ += bytes_allocated;
// Dirty the card at the destionation as it may contain
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 4e38335c40..48ae84dda7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -96,6 +96,7 @@ static const size_t kDefaultMarkStackSize = 64 * KB;
static const char* kDlMallocSpaceName[2] = {"main dlmalloc space", "main dlmalloc space 1"};
static const char* kRosAllocSpaceName[2] = {"main rosalloc space", "main rosalloc space 1"};
static const char* kMemMapSpaceName[2] = {"main space", "main space 1"};
+static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB;
Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
double target_utilization, double foreground_heap_growth_multiplier, size_t capacity,
@@ -179,16 +180,16 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
running_on_valgrind_(Runtime::Current()->RunningOnValgrind()),
use_tlab_(use_tlab),
main_space_backup_(nullptr),
- min_interval_homogeneous_space_compaction_by_oom_(min_interval_homogeneous_space_compaction_by_oom),
+ min_interval_homogeneous_space_compaction_by_oom_(
+ min_interval_homogeneous_space_compaction_by_oom),
last_time_homogeneous_space_compaction_by_oom_(NanoTime()),
use_homogeneous_space_compaction_for_oom_(use_homogeneous_space_compaction_for_oom) {
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
- const bool is_zygote = Runtime::Current()->IsZygote();
// If we aren't the zygote, switch to the default non zygote allocator. This may update the
// entrypoints.
- if (!is_zygote) {
+ if (!Runtime::Current()->IsZygote()) {
large_object_threshold_ = kDefaultLargeObjectThreshold;
// Background compaction is currently not supported for command line runs.
if (background_collector_type_ != foreground_collector_type_) {
@@ -197,7 +198,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
}
}
ChangeCollector(desired_collector_type_);
-
live_bitmap_.reset(new accounting::HeapBitmap(this));
mark_bitmap_.reset(new accounting::HeapBitmap(this));
// Requested begin for the alloc space, to follow the mapped image and oat files
@@ -213,130 +213,117 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
CHECK_GT(oat_file_end_addr, image_space->End());
requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize);
}
-
/*
requested_alloc_space_begin -> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+- nonmoving space (kNonMovingSpaceCapacity) +-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
- +- main alloc space (capacity_) +-
+ +-main alloc space / bump space 1 (capacity_) +-
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ +-????????????????????????????????????????????+-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
- +- main alloc space 1 (capacity_) +-
+ +-main alloc space2 / bump space 2 (capacity_)+-
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*/
- bool create_backup_main_space =
+ bool support_homogeneous_space_compaction =
background_collector_type == gc::kCollectorTypeHomogeneousSpaceCompact ||
use_homogeneous_space_compaction_for_oom;
- if (is_zygote) {
- // Reserve the address range before we create the non moving space to make sure bitmaps don't
- // take it.
- std::string error_str;
- MemMap* main_space_map = MemMap::MapAnonymous(
- kMemMapSpaceName[0], requested_alloc_space_begin + kNonMovingSpaceCapacity, capacity_,
- PROT_READ | PROT_WRITE, true, &error_str);
- CHECK(main_space_map != nullptr) << error_str;
- MemMap* main_space_1_map = nullptr;
- // Attempt to reserve an extra mem_map for homogeneous space compaction right after the main space map.
- if (create_backup_main_space) {
- main_space_1_map = MemMap::MapAnonymous(kMemMapSpaceName[1], main_space_map->End(), capacity_,
- PROT_READ | PROT_WRITE, true, &error_str);
- if (main_space_1_map == nullptr) {
- LOG(WARNING) << "Failed to create map " << kMemMapSpaceName[1] << " with error "
- << error_str;
- }
- }
+ // We may use the same space the main space for the non moving space if we don't need to compact
+ // from the main space.
+ // This is not the case if we support homogeneous compaction or have a moving background
+ // collector type.
+ const bool is_zygote = Runtime::Current()->IsZygote();
+ bool separate_non_moving_space = is_zygote ||
+ support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) ||
+ IsMovingGc(background_collector_type_);
+ if (foreground_collector_type == kCollectorTypeGSS) {
+ separate_non_moving_space = false;
+ }
+ std::unique_ptr<MemMap> main_mem_map_1;
+ std::unique_ptr<MemMap> main_mem_map_2;
+ byte* request_begin = requested_alloc_space_begin;
+ if (request_begin != nullptr && separate_non_moving_space) {
+ request_begin += kNonMovingSpaceCapacity;
+ }
+ std::string error_str;
+ std::unique_ptr<MemMap> non_moving_space_mem_map;
+ if (separate_non_moving_space) {
+ // Reserve the non moving mem map before the other two since it needs to be at a specific
+ // address.
+ non_moving_space_mem_map.reset(
+ MemMap::MapAnonymous("non moving space", requested_alloc_space_begin,
+ kNonMovingSpaceCapacity, PROT_READ | PROT_WRITE, true, &error_str));
+ CHECK(non_moving_space_mem_map != nullptr) << error_str;
+ }
+ // Attempt to create 2 mem maps at or after the requested begin.
+ main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_,
+ PROT_READ | PROT_WRITE, &error_str));
+ CHECK(main_mem_map_1.get() != nullptr) << error_str;
+ if (support_homogeneous_space_compaction ||
+ background_collector_type_ == kCollectorTypeSS ||
+ foreground_collector_type_ == kCollectorTypeSS) {
+ main_mem_map_2.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[1], main_mem_map_1->End(),
+ capacity_, PROT_READ | PROT_WRITE,
+ &error_str));
+ CHECK(main_mem_map_2.get() != nullptr) << error_str;
+ }
+ // Create the non moving space first so that bitmaps don't take up the address range.
+ if (separate_non_moving_space) {
// Non moving space is always dlmalloc since we currently don't have support for multiple
// active rosalloc spaces.
- non_moving_space_ = space::DlMallocSpace::Create(
- "zygote / non moving space", initial_size, kNonMovingSpaceCapacity,
- kNonMovingSpaceCapacity, requested_alloc_space_begin, false);
+ const size_t size = non_moving_space_mem_map->Size();
+ non_moving_space_ = space::DlMallocSpace::CreateFromMemMap(
+ non_moving_space_mem_map.release(), "zygote / non moving space", initial_size,
+ initial_size, size, size, false);
non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
- CreateMainMallocSpace(main_space_map, initial_size, growth_limit_, capacity_);
- if (main_space_1_map != nullptr) {
- const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
- main_space_backup_ = CreateMallocSpaceFromMemMap(main_space_1_map, initial_size,
- growth_limit_, capacity_, name, true);
- }
- } else {
- std::string error_str;
- byte* request_begin = requested_alloc_space_begin;
- if (request_begin == nullptr) {
- // Disable homogeneous space compaction since we don't have an image.
- create_backup_main_space = false;
- }
- MemMap* main_space_1_map = nullptr;
- if (create_backup_main_space) {
- request_begin += kNonMovingSpaceCapacity;
- // Attempt to reserve an extra mem_map for homogeneous space compaction right after the main space map.
- main_space_1_map = MemMap::MapAnonymous(kMemMapSpaceName[1], request_begin + capacity_,
- capacity_, PROT_READ | PROT_WRITE, true, &error_str);
- if (main_space_1_map == nullptr) {
- LOG(WARNING) << "Failed to create map " << kMemMapSpaceName[1] << " with error "
- << error_str;
- request_begin = requested_alloc_space_begin;
- }
- }
- MemMap* main_space_map = MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity_,
- PROT_READ | PROT_WRITE, true, &error_str);
- CHECK(main_space_map != nullptr) << error_str;
- // Introduce a seperate non moving space.
- if (main_space_1_map != nullptr) {
- // Do this before creating the main malloc space to prevent bitmaps from being placed here.
- non_moving_space_ = space::DlMallocSpace::Create(
- "non moving space", kDefaultInitialSize, kNonMovingSpaceCapacity, kNonMovingSpaceCapacity,
- requested_alloc_space_begin, false);
- non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
- }
- // Create the main free list space, which doubles as the non moving space. We can do this since
- // non zygote means that we won't have any background compaction.
- CreateMainMallocSpace(main_space_map, initial_size, growth_limit_, capacity_);
- if (main_space_1_map != nullptr) {
- const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
- main_space_backup_ = CreateMallocSpaceFromMemMap(main_space_1_map, initial_size,
- growth_limit_, capacity_, name, true);
- CHECK(main_space_backup_ != nullptr);
- } else {
- non_moving_space_ = main_space_;
- }
+ CHECK(non_moving_space_ != nullptr) << "Failed creating non moving space "
+ << requested_alloc_space_begin;
+ AddSpace(non_moving_space_);
}
- CHECK(non_moving_space_ != nullptr);
-
- // We need to create the bump pointer if the foreground collector is a compacting GC. We only
- // create the bump pointer space if we are not a moving foreground collector but have a moving
- // background collector since the heap transition code will create the temp space by recycling
- // the bitmap from the main space.
- if (kMovingCollector &&
- (IsMovingGc(foreground_collector_type_) || IsMovingGc(background_collector_type_))) {
+ // Create other spaces based on whether or not we have a moving GC.
+ if (IsMovingGc(foreground_collector_type_) && foreground_collector_type_ != kCollectorTypeGSS) {
+ // Create bump pointer spaces.
+ // We only to create the bump pointer if the foreground collector is a compacting GC.
// TODO: Place bump-pointer spaces somewhere to minimize size of card table.
- // Divide by 2 for a temporary fix for reducing virtual memory usage.
- const size_t bump_pointer_space_capacity = capacity_ / 2;
- bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space",
- bump_pointer_space_capacity, nullptr);
+ bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 1",
+ main_mem_map_1.release());
CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space";
AddSpace(bump_pointer_space_);
- temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2",
- bump_pointer_space_capacity, nullptr);
+ temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
+ main_mem_map_2.release());
CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space";
AddSpace(temp_space_);
- }
- if (non_moving_space_ != main_space_) {
- AddSpace(non_moving_space_);
- }
- if (main_space_backup_ != nullptr) {
- AddSpace(main_space_backup_);
+ CHECK(separate_non_moving_space);
} else {
- const char* disable_msg = "Disabling homogenous space compact due to no backup main space";
- if (background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact) {
- background_collector_type_ = collector_type_;
- LOG(WARNING) << disable_msg;
- } else if (use_homogeneous_space_compaction_for_oom_) {
- LOG(WARNING) << disable_msg;
- }
- use_homogeneous_space_compaction_for_oom_ = false;
- }
- if (main_space_ != nullptr) {
+ CreateMainMallocSpace(main_mem_map_1.release(), initial_size, growth_limit_, capacity_);
+ CHECK(main_space_ != nullptr);
AddSpace(main_space_);
+ if (!separate_non_moving_space) {
+ non_moving_space_ = main_space_;
+ CHECK(!non_moving_space_->CanMoveObjects());
+ }
+ if (foreground_collector_type_ == kCollectorTypeGSS) {
+ CHECK_EQ(foreground_collector_type_, background_collector_type_);
+ // Create bump pointer spaces instead of a backup space.
+ main_mem_map_2.release();
+ bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space 1",
+ kGSSBumpPointerSpaceCapacity, nullptr);
+ CHECK(bump_pointer_space_ != nullptr);
+ AddSpace(bump_pointer_space_);
+ temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2",
+ kGSSBumpPointerSpaceCapacity, nullptr);
+ CHECK(temp_space_ != nullptr);
+ AddSpace(temp_space_);
+ } else if (main_mem_map_2.get() != nullptr) {
+ const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
+ main_space_backup_.reset(CreateMallocSpaceFromMemMap(main_mem_map_2.release(), initial_size,
+ growth_limit_, capacity_, name, true));
+ CHECK(main_space_backup_.get() != nullptr);
+ // Add the space so its accounted for in the heap_begin and heap_end.
+ AddSpace(main_space_backup_.get());
+ }
}
-
+ CHECK(non_moving_space_ != nullptr);
+ CHECK(!non_moving_space_->CanMoveObjects());
// Allocate the large object space.
if (kUseFreeListSpaceForLOS) {
large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_);
@@ -345,19 +332,19 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
}
CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
AddSpace(large_object_space_);
-
// Compute heap capacity. Continuous spaces are sorted in order of Begin().
CHECK(!continuous_spaces_.empty());
-
// Relies on the spaces being sorted.
byte* heap_begin = continuous_spaces_.front()->Begin();
byte* heap_end = continuous_spaces_.back()->Limit();
size_t heap_capacity = heap_end - heap_begin;
-
+ // Remove the main backup space since it slows down the GC to have unused extra spaces.
+ if (main_space_backup_.get() != nullptr) {
+ RemoveSpace(main_space_backup_.get());
+ }
// Allocate the card table.
card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity));
CHECK(card_table_.get() != NULL) << "Failed to create card table";
-
// Card cache for now since it makes it easier for us to update the references to the copying
// spaces.
accounting::ModUnionTable* mod_union_table =
@@ -365,17 +352,14 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
GetImageSpace());
CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
AddModUnionTable(mod_union_table);
-
if (collector::SemiSpace::kUseRememberedSet && non_moving_space_ != main_space_) {
accounting::RememberedSet* non_moving_space_rem_set =
new accounting::RememberedSet("Non-moving space remembered set", this, non_moving_space_);
CHECK(non_moving_space_rem_set != nullptr) << "Failed to create non-moving space remembered set";
AddRememberedSet(non_moving_space_rem_set);
}
-
- // TODO: Count objects in the image space here.
+ // TODO: Count objects in the image space here?
num_bytes_allocated_.StoreRelaxed(0);
-
mark_stack_.reset(accounting::ObjectStack::Create("mark stack", kDefaultMarkStackSize,
kDefaultMarkStackSize));
const size_t alloc_stack_capacity = max_allocation_stack_size_ + kAllocationStackReserveSize;
@@ -383,7 +367,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
"allocation stack", max_allocation_stack_size_, alloc_stack_capacity));
live_stack_.reset(accounting::ObjectStack::Create(
"live stack", max_allocation_stack_size_, alloc_stack_capacity));
-
// It's still too early to take a lock because there are no threads yet, but we can create locks
// now. We don't create it earlier to make it clear that you can't use locks during heap
// initialization.
@@ -392,13 +375,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
*gc_complete_lock_));
heap_trim_request_lock_ = new Mutex("Heap trim request lock");
last_gc_size_ = GetBytesAllocated();
-
if (ignore_max_footprint_) {
SetIdealFootprint(std::numeric_limits<size_t>::max());
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
}
CHECK_NE(max_allowed_footprint_, 0U);
-
// Create our garbage collectors.
for (size_t i = 0; i < 2; ++i) {
const bool concurrent = i != 0;
@@ -417,26 +398,38 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
mark_compact_collector_ = new collector::MarkCompact(this);
garbage_collectors_.push_back(mark_compact_collector_);
}
-
- if (GetImageSpace() != nullptr && main_space_ != nullptr) {
- // Check that there's no gap between the image space and the main space so that the immune
- // region won't break (eg. due to a large object allocated in the gap).
- bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(), main_space_->GetMemMap());
+ if (GetImageSpace() != nullptr && non_moving_space_ != nullptr) {
+ // Check that there's no gap between the image space and the non moving space so that the
+ // immune region won't break (eg. due to a large object allocated in the gap).
+ bool no_gap = MemMap::CheckNoGaps(GetImageSpace()->GetMemMap(),
+ non_moving_space_->GetMemMap());
if (!no_gap) {
MemMap::DumpMaps(LOG(ERROR));
LOG(FATAL) << "There's a gap between the image space and the main space";
}
}
-
if (running_on_valgrind_) {
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
}
-
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() exiting";
}
}
+MemMap* Heap::MapAnonymousPreferredAddress(const char* name, byte* request_begin, size_t capacity,
+ int prot_flags, std::string* out_error_str) {
+ while (true) {
+ MemMap* map = MemMap::MapAnonymous(kMemMapSpaceName[0], request_begin, capacity,
+ PROT_READ | PROT_WRITE, true, out_error_str);
+ if (map != nullptr || request_begin == nullptr) {
+ return map;
+ }
+ // Retry a second time with no specified request begin.
+ request_begin = nullptr;
+ }
+ return nullptr;
+}
+
space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size,
size_t growth_limit, size_t capacity,
const char* name, bool can_move_objects) {
@@ -474,7 +467,8 @@ void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t gr
if (kCompactZygote && Runtime::Current()->IsZygote() && !can_move_objects) {
// After the zygote we want this to be false if we don't have background compaction enabled so
// that getting primitive array elements is faster.
- can_move_objects = !have_zygote_space_;
+ // We never have homogeneous compaction with GSS and don't need a space with movable objects.
+ can_move_objects = !have_zygote_space_ && foreground_collector_type_ != kCollectorTypeGSS;
}
if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) {
RemoveRememberedSet(main_space_);
@@ -899,12 +893,15 @@ void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType
<< " free bytes";
// If the allocation failed due to fragmentation, print out the largest continuous allocation.
if (total_bytes_free >= byte_count) {
- space::MallocSpace* space = nullptr;
+ space::AllocSpace* space = nullptr;
if (allocator_type == kAllocatorTypeNonMoving) {
space = non_moving_space_;
} else if (allocator_type == kAllocatorTypeRosAlloc ||
allocator_type == kAllocatorTypeDlMalloc) {
space = main_space_;
+ } else if (allocator_type == kAllocatorTypeBumpPointer ||
+ allocator_type == kAllocatorTypeTLAB) {
+ space = bump_pointer_space_;
}
if (space != nullptr) {
space->LogFragmentationAllocFailure(oss, byte_count);
@@ -1512,15 +1509,18 @@ HomogeneousSpaceCompactResult Heap::PerformHomogeneousSpaceCompact() {
tl->SuspendAll();
uint64_t start_time = NanoTime();
// Launch compaction.
- space::MallocSpace* to_space = main_space_backup_;
+ space::MallocSpace* to_space = main_space_backup_.release();
space::MallocSpace* from_space = main_space_;
to_space->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
const uint64_t space_size_before_compaction = from_space->Size();
+ AddSpace(to_space);
Compact(to_space, from_space, kGcCauseHomogeneousSpaceCompact);
// Leave as prot read so that we can still run ROSAlloc verification on this space.
from_space->GetMemMap()->Protect(PROT_READ);
const uint64_t space_size_after_compaction = to_space->Size();
- std::swap(main_space_, main_space_backup_);
+ main_space_ = to_space;
+ main_space_backup_.reset(from_space);
+ RemoveSpace(from_space);
SetSpaceAsDefault(main_space_); // Set as default to reset the proper dlmalloc space.
// Update performed homogeneous space compaction count.
count_performed_homogeneous_space_compaction_++;
@@ -1587,17 +1587,27 @@ void Heap::TransitionCollector(CollectorType collector_type) {
}
tl->SuspendAll();
switch (collector_type) {
- case kCollectorTypeSS:
- // Fall-through.
- case kCollectorTypeGSS: {
+ case kCollectorTypeSS: {
if (!IsMovingGc(collector_type_)) {
+ // Create the bump pointer space from the backup space.
+ CHECK(main_space_backup_ != nullptr);
+ std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
// We are transitioning from non moving GC -> moving GC, since we copied from the bump
// pointer space last transition it will be protected.
- bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
+ CHECK(mem_map != nullptr);
+ mem_map->Protect(PROT_READ | PROT_WRITE);
+ bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
+ mem_map.release());
+ AddSpace(bump_pointer_space_);
Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
+ // Use the now empty main space mem map for the bump pointer temp space.
+ mem_map.reset(main_space_->ReleaseMemMap());
// Remove the main space so that we don't try to trim it, this doens't work for debug
// builds since RosAlloc attempts to read the magic number from a protected page.
RemoveSpace(main_space_);
+ temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
+ mem_map.release());
+ AddSpace(temp_space_);
}
break;
}
@@ -1605,10 +1615,25 @@ void Heap::TransitionCollector(CollectorType collector_type) {
// Fall through.
case kCollectorTypeCMS: {
if (IsMovingGc(collector_type_)) {
+ CHECK(temp_space_ != nullptr);
+ std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
+ RemoveSpace(temp_space_);
+ temp_space_ = nullptr;
+ CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize, mem_map->Size(),
+ mem_map->Size());
+ mem_map.release();
// Compact to the main space from the bump pointer space, don't need to swap semispaces.
AddSpace(main_space_);
main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
+ mem_map.reset(bump_pointer_space_->ReleaseMemMap());
+ RemoveSpace(bump_pointer_space_);
+ bump_pointer_space_ = nullptr;
+ const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
+ main_space_backup_.reset(CreateMallocSpaceFromMemMap(mem_map.get(), kDefaultInitialSize,
+ mem_map->Size(), mem_map->Size(),
+ name, true));
+ mem_map.release();
}
break;
}
@@ -1811,6 +1836,7 @@ void Heap::PreZygoteFork() {
// there.
non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
// Change the collector to the post zygote one.
+ bool same_space = non_moving_space_ == main_space_;
if (kCompactZygote) {
DCHECK(semi_space_collector_ != nullptr);
// Temporarily disable rosalloc verification because the zygote
@@ -1877,6 +1903,11 @@ void Heap::PreZygoteFork() {
space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space",
low_memory_mode_,
&non_moving_space_);
+ CHECK(!non_moving_space_->CanMoveObjects());
+ if (same_space) {
+ main_space_ = non_moving_space_;
+ SetSpaceAsDefault(main_space_);
+ }
delete old_alloc_space;
CHECK(zygote_space != nullptr) << "Failed creating zygote space";
AddSpace(zygote_space);
@@ -2178,7 +2209,7 @@ class VerifyReferenceVisitor {
LOG(ERROR) << "Object " << obj << " class(" << obj->GetClass() << ") not a heap address";
}
- // Attmept to find the class inside of the recently freed objects.
+ // Attempt to find the class inside of the recently freed objects.
space::ContinuousSpace* ref_space = heap_->FindContinuousSpaceFromObject(ref, true);
if (ref_space != nullptr && ref_space->IsMallocSpace()) {
space::MallocSpace* space = ref_space->AsMallocSpace();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 0da113f2c6..56c6d6d3c1 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -444,8 +444,7 @@ class Heap {
bool fail_ok) const;
space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const;
- void DumpForSigQuit(std::ostream& os);
-
+ void DumpForSigQuit(std::ostream& os) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
// Do a pending heap transition or trim.
void DoPendingTransitionOrTrim() LOCKS_EXCLUDED(heap_trim_request_lock_);
@@ -594,8 +593,13 @@ class Heap {
void FinishGC(Thread* self, collector::GcType gc_type) LOCKS_EXCLUDED(gc_complete_lock_);
+ // Create a mem map with a preferred base address.
+ static MemMap* MapAnonymousPreferredAddress(const char* name, byte* request_begin,
+ size_t capacity, int prot_flags,
+ std::string* out_error_str);
+
bool SupportHSpaceCompaction() const {
- // Returns true if we can do hspace compaction.
+ // Returns true if we can do hspace compaction
return main_space_backup_ != nullptr;
}
@@ -1007,7 +1011,8 @@ class Heap {
const bool use_tlab_;
// Pointer to the space which becomes the new main space when we do homogeneous space compaction.
- space::MallocSpace* main_space_backup_;
+ // Use unique_ptr since the space is only added during the homogeneous compaction phase.
+ std::unique_ptr<space::MallocSpace> main_space_backup_;
// Minimal interval allowed between two homogeneous space compactions caused by OOM.
uint64_t min_interval_homogeneous_space_compaction_by_oom_;
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 8b3569232a..fb6bbac6d0 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -258,6 +258,14 @@ bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
return true;
}
+void BumpPointerSpace::LogFragmentationAllocFailure(std::ostream& os,
+ size_t /* failed_alloc_bytes */) {
+ size_t max_contiguous_allocation = Limit() - End();
+ os << "; failed due to fragmentation (largest possible contiguous allocation "
+ << max_contiguous_allocation << " bytes)";
+ // Caller's job to print failed_alloc_bytes.
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index feee34f8bb..71b15baff1 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -151,6 +151,9 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace {
bytes_allocated_.FetchAndSubSequentiallyConsistent(bytes);
}
+ void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Object alignment within the space.
static constexpr size_t kAlignment = 8;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index a87aa890c8..7a3cbf3dbf 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -16,6 +16,8 @@
#include "image_space.h"
+#include <random>
+
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "base/scoped_flock.h"
@@ -94,86 +96,304 @@ static bool GenerateImage(const std::string& image_filename, std::string* error_
bool ImageSpace::FindImageFilename(const char* image_location,
const InstructionSet image_isa,
- std::string* image_filename,
- bool *is_system) {
+ std::string* system_filename,
+ bool* has_system,
+ std::string* cache_filename,
+ bool* dalvik_cache_exists,
+ bool* has_cache) {
+ *has_system = false;
+ *has_cache = false;
// image_location = /system/framework/boot.art
// system_image_location = /system/framework/<image_isa>/boot.art
std::string system_image_filename(GetSystemImageFilename(image_location, image_isa));
if (OS::FileExists(system_image_filename.c_str())) {
- *image_filename = system_image_filename;
- *is_system = true;
+ *system_filename = system_image_filename;
+ *has_system = true;
+ }
+
+ bool have_android_data = false;
+ *dalvik_cache_exists = false;
+ std::string dalvik_cache;
+ GetDalvikCache(GetInstructionSetString(image_isa), true, &dalvik_cache,
+ &have_android_data, dalvik_cache_exists);
+
+ if (have_android_data && *dalvik_cache_exists) {
+ // Always set output location even if it does not exist,
+ // so that the caller knows where to create the image.
+ //
+ // image_location = /system/framework/boot.art
+ // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
+ std::string error_msg;
+ if (!GetDalvikCacheFilename(image_location, dalvik_cache.c_str(), cache_filename, &error_msg)) {
+ LOG(WARNING) << error_msg;
+ return *has_system;
+ }
+ *has_cache = OS::FileExists(cache_filename->c_str());
+ }
+ return *has_system || *has_cache;
+}
+
+static bool ReadSpecificImageHeader(const char* filename, ImageHeader* image_header) {
+ std::unique_ptr<File> image_file(OS::OpenFileForReading(filename));
+ if (image_file.get() == nullptr) {
+ return false;
+ }
+ const bool success = image_file->ReadFully(image_header, sizeof(ImageHeader));
+ if (!success || !image_header->IsValid()) {
+ return false;
+ }
return true;
+}
+
+static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) {
+ CHECK_ALIGNED(min_delta, kPageSize);
+ CHECK_ALIGNED(max_delta, kPageSize);
+ CHECK_LT(min_delta, max_delta);
+
+ std::default_random_engine generator;
+ generator.seed(NanoTime() * getpid());
+ std::uniform_int_distribution<int32_t> distribution(min_delta, max_delta);
+ int32_t r = distribution(generator);
+ if (r % 2 == 0) {
+ r = RoundUp(r, kPageSize);
+ } else {
+ r = RoundDown(r, kPageSize);
}
+ CHECK_LE(min_delta, r);
+ CHECK_GE(max_delta, r);
+ CHECK_ALIGNED(r, kPageSize);
+ return r;
+}
- const std::string dalvik_cache = GetDalvikCacheOrDie(GetInstructionSetString(image_isa));
+bool ImageSpace::RelocateImage(const char* image_location, const char* dest_filename,
+ InstructionSet isa, std::string* error_msg) {
+ std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
- // Always set output location even if it does not exist,
- // so that the caller knows where to create the image.
- //
- // image_location = /system/framework/boot.art
- // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
- *image_filename = GetDalvikCacheFilenameOrDie(image_location, dalvik_cache.c_str());
- *is_system = false;
- return OS::FileExists(image_filename->c_str());
+ std::string input_image_location_arg("--input-image-location=");
+ input_image_location_arg += image_location;
+
+ std::string output_image_filename_arg("--output-image-file=");
+ output_image_filename_arg += dest_filename;
+
+ std::string input_oat_location_arg("--input-oat-location=");
+ input_oat_location_arg += ImageHeader::GetOatLocationFromImageLocation(image_location);
+
+ std::string output_oat_filename_arg("--output-oat-file=");
+ output_oat_filename_arg += ImageHeader::GetOatLocationFromImageLocation(dest_filename);
+
+ std::string instruction_set_arg("--instruction-set=");
+ instruction_set_arg += GetInstructionSetString(isa);
+
+ std::string base_offset_arg("--base-offset-delta=");
+ StringAppendF(&base_offset_arg, "%d", ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA,
+ ART_BASE_ADDRESS_MAX_DELTA));
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+
+ argv.push_back(input_image_location_arg);
+ argv.push_back(output_image_filename_arg);
+
+ argv.push_back(input_oat_location_arg);
+ argv.push_back(output_oat_filename_arg);
+
+ argv.push_back(instruction_set_arg);
+ argv.push_back(base_offset_arg);
+
+ std::string command_line(Join(argv, ' '));
+ LOG(INFO) << "RelocateImage: " << command_line;
+ return Exec(argv, error_msg);
+}
+
+static ImageHeader* ReadSpecificImageHeaderOrDie(const char* filename) {
+ std::unique_ptr<ImageHeader> hdr(new ImageHeader);
+ if (!ReadSpecificImageHeader(filename, hdr.get())) {
+ LOG(FATAL) << "Unable to read image header for " << filename;
+ return nullptr;
+ }
+ return hdr.release();
}
ImageHeader* ImageSpace::ReadImageHeaderOrDie(const char* image_location,
const InstructionSet image_isa) {
- std::string image_filename;
- bool is_system = false;
- if (FindImageFilename(image_location, image_isa, &image_filename, &is_system)) {
- std::unique_ptr<File> image_file(OS::OpenFileForReading(image_filename.c_str()));
- std::unique_ptr<ImageHeader> image_header(new ImageHeader);
- const bool success = image_file->ReadFully(image_header.get(), sizeof(ImageHeader));
- if (!success || !image_header->IsValid()) {
- LOG(FATAL) << "Invalid Image header for: " << image_filename;
- return nullptr;
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ if (FindImageFilename(image_location, image_isa, &system_filename, &has_system,
+ &cache_filename, &dalvik_cache_exists, &has_cache)) {
+ if (Runtime::Current()->ShouldRelocate()) {
+ if (has_system && has_cache) {
+ std::unique_ptr<ImageHeader> sys_hdr(new ImageHeader);
+ std::unique_ptr<ImageHeader> cache_hdr(new ImageHeader);
+ if (!ReadSpecificImageHeader(system_filename.c_str(), sys_hdr.get())) {
+ LOG(FATAL) << "Unable to read image header for " << image_location << " at "
+ << system_filename;
+ return nullptr;
+ }
+ if (!ReadSpecificImageHeader(cache_filename.c_str(), cache_hdr.get())) {
+ LOG(FATAL) << "Unable to read image header for " << image_location << " at "
+ << cache_filename;
+ return nullptr;
+ }
+ if (sys_hdr->GetOatChecksum() != cache_hdr->GetOatChecksum()) {
+ LOG(FATAL) << "Unable to find a relocated version of image file " << image_location;
+ return nullptr;
+ }
+ return cache_hdr.release();
+ } else if (!has_cache) {
+ LOG(FATAL) << "Unable to find a relocated version of image file " << image_location;
+ return nullptr;
+ } else if (!has_system && has_cache) {
+ // This can probably just use the cache one.
+ return ReadSpecificImageHeaderOrDie(cache_filename.c_str());
+ }
+ } else {
+ // We don't want to relocate, Just pick the appropriate one if we have it and return.
+ if (has_system && has_cache) {
+ // We want the cache if the checksum matches, otherwise the system.
+ std::unique_ptr<ImageHeader> system(ReadSpecificImageHeaderOrDie(system_filename.c_str()));
+ std::unique_ptr<ImageHeader> cache(ReadSpecificImageHeaderOrDie(cache_filename.c_str()));
+ if (system.get() == nullptr ||
+ (cache.get() != nullptr && cache->GetOatChecksum() == system->GetOatChecksum())) {
+ return cache.release();
+ } else {
+ return system.release();
+ }
+ } else if (has_system) {
+ return ReadSpecificImageHeaderOrDie(system_filename.c_str());
+ } else if (has_cache) {
+ return ReadSpecificImageHeaderOrDie(cache_filename.c_str());
+ }
}
-
- return image_header.release();
}
LOG(FATAL) << "Unable to find image file for: " << image_location;
return nullptr;
}
+static bool ChecksumsMatch(const char* image_a, const char* image_b) {
+ ImageHeader hdr_a;
+ ImageHeader hdr_b;
+ return ReadSpecificImageHeader(image_a, &hdr_a) && ReadSpecificImageHeader(image_b, &hdr_b)
+ && hdr_a.GetOatChecksum() == hdr_b.GetOatChecksum();
+}
+
ImageSpace* ImageSpace::Create(const char* image_location,
const InstructionSet image_isa) {
- std::string image_filename;
std::string error_msg;
- bool is_system = false;
- const bool found_image = FindImageFilename(image_location, image_isa, &image_filename,
- &is_system);
-
- // Note that we must not use the file descriptor associated with
- // ScopedFlock::GetFile to Init the image file. We want the file
- // descriptor (and the associated exclusive lock) to be released when
- // we leave Create.
- ScopedFlock image_lock;
- image_lock.Init(image_filename.c_str(), &error_msg);
-
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
+ &has_system, &cache_filename, &dalvik_cache_exists,
+ &has_cache);
+
+ ImageSpace* space;
+ bool relocate = Runtime::Current()->ShouldRelocate();
if (found_image) {
- ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, !is_system,
- &error_msg);
+ const std::string* image_filename;
+ bool is_system = false;
+ bool relocated_version_used = false;
+ if (relocate) {
+ CHECK(dalvik_cache_exists) << "Requiring relocation for image " << image_location << " "
+ << "at " << system_filename << " but we do not have any "
+ << "dalvik_cache to find/place it in.";
+ if (has_system) {
+ if (has_cache && ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
+ // We already have a relocated version
+ image_filename = &cache_filename;
+ relocated_version_used = true;
+ } else {
+ // We cannot have a relocated version, Relocate the system one and use it.
+ if (RelocateImage(image_location, cache_filename.c_str(), image_isa,
+ &error_msg)) {
+ relocated_version_used = true;
+ image_filename = &cache_filename;
+ } else {
+ LOG(FATAL) << "Unable to relocate image " << image_location << " "
+ << "from " << system_filename << " to " << cache_filename << ": "
+ << error_msg;
+ return nullptr;
+ }
+ }
+ } else {
+ CHECK(has_cache);
+ // We can just use cache's since it should be fine. This might or might not be relocated.
+ image_filename = &cache_filename;
+ }
+ } else {
+ if (has_system && has_cache) {
+ // Check they have the same cksum. If they do use the cache. Otherwise system.
+ if (ChecksumsMatch(system_filename.c_str(), cache_filename.c_str())) {
+ image_filename = &cache_filename;
+ relocated_version_used = true;
+ } else {
+ image_filename = &system_filename;
+ }
+ } else if (has_system) {
+ image_filename = &system_filename;
+ } else {
+ CHECK(has_cache);
+ image_filename = &cache_filename;
+ }
+ }
+ {
+ // Note that we must not use the file descriptor associated with
+ // ScopedFlock::GetFile to Init the image file. We want the file
+ // descriptor (and the associated exclusive lock) to be released when
+ // we leave Create.
+ ScopedFlock image_lock;
+ image_lock.Init(image_filename->c_str(), &error_msg);
+ LOG(INFO) << "Using image file " << image_filename->c_str() << " for image location "
+ << image_location;
+ space = ImageSpace::Init(image_filename->c_str(), image_location,
+ false, &error_msg);
+ }
if (space != nullptr) {
return space;
}
- // If the /system file exists, it should be up-to-date, don't try to generate it.
- // If it's not the /system file, log a warning and fall through to GenerateImage.
- if (is_system) {
- LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+ // If the /system file exists, it should be up-to-date, don't try to generate it. Same if it is
+ // a relocated copy from something in /system (i.e. checksum's match).
+ // Otherwise, log a warning and fall through to GenerateImage.
+ if (relocated_version_used) {
+ LOG(FATAL) << "Attempted to use relocated version of " << image_location << " "
+ << "at " << cache_filename << " generated from " << system_filename << " "
+ << "but image failed to load: " << error_msg;
+ return nullptr;
+ } else if (is_system) {
+ LOG(FATAL) << "Failed to load /system image '" << *image_filename << "': " << error_msg;
return nullptr;
} else {
LOG(WARNING) << error_msg;
}
}
- CHECK(GenerateImage(image_filename, &error_msg))
- << "Failed to generate image '" << image_filename << "': " << error_msg;
- ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, true, &error_msg);
+ CHECK(dalvik_cache_exists) << "No place to put generated image.";
+ CHECK(GenerateImage(cache_filename, &error_msg))
+ << "Failed to generate image '" << cache_filename << "': " << error_msg;
+ // TODO Should I relocate this image? Sure
+ if (relocate) {
+ if (!RelocateImage(cache_filename.c_str(), cache_filename.c_str(), image_isa, &error_msg)) {
+ LOG(FATAL) << "Failed to relocate newly created image " << cache_filename.c_str();
+ return nullptr;
+ }
+ }
+ {
+ // Note that we must not use the file descriptor associated with
+ // ScopedFlock::GetFile to Init the image file. We want the file
+ // descriptor (and the associated exclusive lock) to be released when
+ // we leave Create.
+ ScopedFlock image_lock;
+ image_lock.Init(cache_filename.c_str(), &error_msg);
+ space = ImageSpace::Init(cache_filename.c_str(), image_location, true, &error_msg);
+ }
if (space == nullptr) {
- LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+ LOG(FATAL) << "Failed to load generated image '" << cache_filename << "': " << error_msg;
}
return space;
}
@@ -316,6 +536,15 @@ OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg)
" in image %s", oat_checksum, image_oat_checksum, GetName());
return nullptr;
}
+ int32_t image_patch_delta = image_header.GetPatchDelta();
+ int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
+ if (oat_patch_delta != image_patch_delta) {
+ // We should have already relocated by this point. Bail out.
+ *error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
+ "in image %s", oat_patch_delta, image_patch_delta, GetName());
+ return nullptr;
+ }
+
return oat_file;
}
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index dd9b58084d..debca52e5b 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -98,6 +98,20 @@ class ImageSpace : public MemMapSpace {
return false;
}
+ // Returns the filename of the image corresponding to
+ // requested image_location, or the filename where a new image
+ // should be written if one doesn't exist. Looks for a generated
+ // image in the specified location and then in the dalvik-cache.
+ //
+ // Returns true if an image was found, false otherwise.
+ static bool FindImageFilename(const char* image_location,
+ InstructionSet image_isa,
+ std::string* system_location,
+ bool* has_system,
+ std::string* data_location,
+ bool* dalvik_cache_exists,
+ bool* has_data);
+
private:
// Tries to initialize an ImageSpace from the given image path,
// returning NULL on error.
@@ -110,16 +124,8 @@ class ImageSpace : public MemMapSpace {
bool validate_oat_file, std::string* error_msg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Returns the filename of the image corresponding to
- // requested image_location, or the filename where a new image
- // should be written if one doesn't exist. Looks for a generated
- // image in the specified location and then in the dalvik-cache.
- //
- // Returns true if an image was found, false otherwise.
- static bool FindImageFilename(const char* image_location,
- InstructionSet image_isa,
- std::string* location,
- bool* is_system);
+ static bool RelocateImage(const char* image_location, const char* dest_filename,
+ InstructionSet isa, std::string* error_msg);
OatFile* OpenOatFile(const char* image, std::string* error_msg) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index abae8ff346..d5a03c66ec 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -431,6 +431,11 @@ collector::ObjectBytePair LargeObjectSpace::Sweep(bool swap_bitmaps) {
return scc.freed;
}
+void LargeObjectSpace::LogFragmentationAllocFailure(std::ostream& /*os*/,
+ size_t /*failed_alloc_bytes*/) {
+ UNIMPLEMENTED(FATAL);
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 01982d06ab..b1c20ca9e6 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -89,6 +89,9 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace {
return end_;
}
+ void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
protected:
explicit LargeObjectSpace(const std::string& name, byte* begin, byte* end);
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 6f49fbf203..a52b92b084 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -133,8 +133,6 @@ class MallocSpace : public ContinuousMemMapAllocSpace {
return can_move_objects_;
}
- virtual void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) = 0;
-
protected:
MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end,
byte* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects,
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index 71c8eb5a84..523d4fe8fd 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -223,6 +223,8 @@ class AllocSpace {
// threads, if the alloc space implementation uses any.
virtual void RevokeAllThreadLocalBuffers() = 0;
+ virtual void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) = 0;
+
protected:
struct SweepCallbackContext {
SweepCallbackContext(bool swap_bitmaps, space::Space* space);
diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc
index fb3a12efb5..51d84f5acb 100644
--- a/runtime/gc/space/zygote_space.cc
+++ b/runtime/gc/space/zygote_space.cc
@@ -77,25 +77,30 @@ void ZygoteSpace::Dump(std::ostream& os) const {
mirror::Object* ZygoteSpace::Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size) {
- LOG(FATAL) << "Unimplemented";
+ UNIMPLEMENTED(FATAL);
return nullptr;
}
size_t ZygoteSpace::AllocationSize(mirror::Object* obj, size_t* usable_size) {
- LOG(FATAL) << "Unimplemented";
+ UNIMPLEMENTED(FATAL);
return 0;
}
size_t ZygoteSpace::Free(Thread* self, mirror::Object* ptr) {
- LOG(FATAL) << "Unimplemented";
+ UNIMPLEMENTED(FATAL);
return 0;
}
size_t ZygoteSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) {
- LOG(FATAL) << "Unimplemented";
+ UNIMPLEMENTED(FATAL);
return 0;
}
+void ZygoteSpace::LogFragmentationAllocFailure(std::ostream& /*os*/,
+ size_t /*failed_alloc_bytes*/) {
+ UNIMPLEMENTED(FATAL);
+}
+
void ZygoteSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) {
SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg);
DCHECK(context->space->IsZygoteSpace());
diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h
index 5d5fe76b74..0cf4bb139c 100644
--- a/runtime/gc/space/zygote_space.h
+++ b/runtime/gc/space/zygote_space.h
@@ -74,6 +74,9 @@ class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace {
return false;
}
+ void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
protected:
virtual accounting::ContinuousSpaceBitmap::SweepCallback* GetSweepCallback() {
return &SweepCallback;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 7e3b6bab26..fd67197986 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -151,7 +151,8 @@ enum HprofHeapTag {
enum HprofHeapId {
HPROF_HEAP_DEFAULT = 0,
HPROF_HEAP_ZYGOTE = 'Z',
- HPROF_HEAP_APP = 'A'
+ HPROF_HEAP_APP = 'A',
+ HPROF_HEAP_IMAGE = 'I',
};
enum HprofBasicType {
@@ -633,8 +634,12 @@ class Hprof {
// U1: NUL-terminated magic string.
fwrite(magic, 1, sizeof(magic), header_fp_);
- // U4: size of identifiers. We're using addresses as IDs, so make sure a pointer fits.
- U4_TO_BUF_BE(buf, 0, sizeof(void*));
+ // U4: size of identifiers. We're using addresses as IDs and our heap references are stored
+ // as uint32_t.
+ // Note of warning: hprof-conv hard-codes the size of identifiers to 4.
+ COMPILE_ASSERT(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t),
+ UnexpectedHeapReferenceSize);
+ U4_TO_BUF_BE(buf, 0, sizeof(uint32_t));
fwrite(buf, 1, sizeof(uint32_t), header_fp_);
// The current time, in milliseconds since 0:00 GMT, 1/1/70.
@@ -842,26 +847,37 @@ static int StackTraceSerialNumber(const mirror::Object* /*obj*/) {
int Hprof::DumpHeapObject(mirror::Object* obj) {
HprofRecord* rec = &current_record_;
- HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
-
+ gc::space::ContinuousSpace* space =
+ Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true);
+ HprofHeapId heap_type = HPROF_HEAP_APP;
+ if (space != nullptr) {
+ if (space->IsZygoteSpace()) {
+ heap_type = HPROF_HEAP_ZYGOTE;
+ } else if (space->IsImageSpace()) {
+ heap_type = HPROF_HEAP_IMAGE;
+ }
+ }
if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) {
StartNewHeapDumpSegment();
}
- if (desiredHeap != current_heap_) {
+ if (heap_type != current_heap_) {
HprofStringId nameId;
// This object is in a different heap than the current one.
// Emit a HEAP_DUMP_INFO tag to change heaps.
rec->AddU1(HPROF_HEAP_DUMP_INFO);
- rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
- switch (desiredHeap) {
+ rec->AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type
+ switch (heap_type) {
case HPROF_HEAP_APP:
nameId = LookupStringId("app");
break;
case HPROF_HEAP_ZYGOTE:
nameId = LookupStringId("zygote");
break;
+ case HPROF_HEAP_IMAGE:
+ nameId = LookupStringId("image");
+ break;
default:
// Internal error
LOG(ERROR) << "Unexpected desiredHeap";
@@ -869,7 +885,7 @@ int Hprof::DumpHeapObject(mirror::Object* obj) {
break;
}
rec->AddStringId(nameId);
- current_heap_ = desiredHeap;
+ current_heap_ = heap_type;
}
mirror::Class* c = obj->GetClass();
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index ad798ed60f..26ddba23f2 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -74,6 +74,7 @@ IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes,
PROT_READ | PROT_WRITE, false, &error_str));
CHECK(table_mem_map_.get() != nullptr) << error_str;
+ CHECK_EQ(table_mem_map_->Size(), table_bytes);
table_ = reinterpret_cast<mirror::Object**>(table_mem_map_->Begin());
CHECK(table_ != nullptr);
diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc
index 5b6039647c..d7e358ce96 100644
--- a/runtime/instruction_set.cc
+++ b/runtime/instruction_set.cc
@@ -83,6 +83,44 @@ size_t GetInstructionSetAlignment(InstructionSet isa) {
}
}
+
+static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
+static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
+
+// TODO: Lower once implicit stack-overflow checks can work with less than 16K.
+static constexpr size_t kArmStackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kArm64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+static constexpr size_t kX86_64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB;
+
+size_t GetStackOverflowReservedBytes(InstructionSet isa) {
+ switch (isa) {
+ case kArm: // Intentional fall-through.
+ case kThumb2:
+ return kArmStackOverflowReservedBytes;
+
+ case kArm64:
+ return kArm64StackOverflowReservedBytes;
+
+ case kMips:
+ return kMipsStackOverflowReservedBytes;
+
+ case kX86:
+ return kX86StackOverflowReservedBytes;
+
+ case kX86_64:
+ return kX86_64StackOverflowReservedBytes;
+
+ case kNone:
+ LOG(FATAL) << "kNone has no stack overflow size";
+ return 0;
+
+ default:
+ LOG(FATAL) << "Unknown instruction set" << isa;
+ return 0;
+ }
+}
+
std::string InstructionSetFeatures::GetFeatureString() const {
std::string result;
if ((mask_ & kHwDiv) != 0) {
diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h
index dce1c150ba..f212811e32 100644
--- a/runtime/instruction_set.h
+++ b/runtime/instruction_set.h
@@ -169,33 +169,7 @@ static inline size_t GetBytesPerFprSpillLocation(InstructionSet isa) {
}
}
-static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
-static constexpr size_t kArmStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
-
-// TODO: shrink reserved space, in particular for 64bit.
-
-// Worst-case, we would need about 2.6x the amount of x86_64 for many more registers.
-// But this one works rather well.
-static constexpr size_t kArm64StackOverflowReservedBytes = 32 * KB;
-// TODO: Bumped to workaround regression (http://b/14982147) Specifically to fix:
-// test-art-host-run-test-interpreter-018-stack-overflow
-// test-art-host-run-test-interpreter-107-int-math2
-static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 32 : 24) * KB;
-static constexpr size_t kX86_64StackOverflowReservedBytes = 32 * KB;
-
-static constexpr size_t GetStackOverflowReservedBytes(InstructionSet isa) {
- return (isa == kArm || isa == kThumb2) ? kArmStackOverflowReservedBytes :
- isa == kArm64 ? kArm64StackOverflowReservedBytes :
- isa == kMips ? kMipsStackOverflowReservedBytes :
- isa == kX86 ? kX86StackOverflowReservedBytes :
- isa == kX86_64 ? kX86_64StackOverflowReservedBytes :
- isa == kNone ? (LOG(FATAL) << "kNone has no stack overflow size", 0) :
- (LOG(FATAL) << "Unknown instruction set" << isa, 0);
-}
-
-static constexpr size_t kRuntimeStackOverflowReservedBytes =
- GetStackOverflowReservedBytes(kRuntimeISA);
+size_t GetStackOverflowReservedBytes(InstructionSet isa);
enum InstructionFeatures {
kHwDiv = 0x1, // Supports hardware divide.
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index f4eaa61c1e..8e375cf4d0 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -519,7 +519,7 @@ void Instrumentation::ConfigureStubs(bool require_entry_exit_stubs, bool require
bool empty;
{
ReaderMutexLock mu(self, deoptimized_methods_lock_);
- empty = deoptimized_methods_.empty(); // Avoid lock violation.
+ empty = IsDeoptimizedMethodsEmpty(); // Avoid lock violation.
}
if (empty) {
instrumentation_stubs_installed_ = false;
@@ -580,7 +580,7 @@ void Instrumentation::ResetQuickAllocEntryPoints() {
}
void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
- const void* portable_code, bool have_portable_code) const {
+ const void* portable_code, bool have_portable_code) {
const void* new_portable_code;
const void* new_quick_code;
bool new_have_portable_code;
@@ -617,20 +617,77 @@ void Instrumentation::UpdateMethodsCode(mirror::ArtMethod* method, const void* q
UpdateEntrypoints(method, new_quick_code, new_portable_code, new_have_portable_code);
}
+bool Instrumentation::AddDeoptimizedMethod(mirror::ArtMethod* method) {
+ // Note that the insert() below isn't read barrier-aware. So, this
+ // FindDeoptimizedMethod() call is necessary or else we would end up
+ // storing the same method twice in the map (the from-space and the
+ // to-space ones).
+ if (FindDeoptimizedMethod(method)) {
+ // Already in the map. Return.
+ return false;
+ }
+ // Not found. Add it.
+ int32_t hash_code = method->IdentityHashCode();
+ deoptimized_methods_.insert(std::make_pair(hash_code, method));
+ return true;
+}
+
+bool Instrumentation::FindDeoptimizedMethod(mirror::ArtMethod* method) {
+ int32_t hash_code = method->IdentityHashCode();
+ auto range = deoptimized_methods_.equal_range(hash_code);
+ for (auto it = range.first; it != range.second; ++it) {
+ mirror::ArtMethod** root = &it->second;
+ mirror::ArtMethod* m = ReadBarrier::BarrierForRoot<mirror::ArtMethod>(root);
+ if (m == method) {
+ // Found.
+ return true;
+ }
+ }
+ // Not found.
+ return false;
+}
+
+mirror::ArtMethod* Instrumentation::BeginDeoptimizedMethod() {
+ auto it = deoptimized_methods_.begin();
+ if (it == deoptimized_methods_.end()) {
+ // Empty.
+ return nullptr;
+ }
+ mirror::ArtMethod** root = &it->second;
+ return ReadBarrier::BarrierForRoot<mirror::ArtMethod>(root);
+}
+
+bool Instrumentation::RemoveDeoptimizedMethod(mirror::ArtMethod* method) {
+ int32_t hash_code = method->IdentityHashCode();
+ auto range = deoptimized_methods_.equal_range(hash_code);
+ for (auto it = range.first; it != range.second; ++it) {
+ mirror::ArtMethod** root = &it->second;
+ mirror::ArtMethod* m = ReadBarrier::BarrierForRoot<mirror::ArtMethod>(root);
+ if (m == method) {
+ // Found. Erase and return.
+ deoptimized_methods_.erase(it);
+ return true;
+ }
+ }
+ // Not found.
+ return false;
+}
+
+bool Instrumentation::IsDeoptimizedMethodsEmpty() const {
+ return deoptimized_methods_.empty();
+}
+
void Instrumentation::Deoptimize(mirror::ArtMethod* method) {
CHECK(!method->IsNative());
CHECK(!method->IsProxyMethod());
CHECK(!method->IsAbstract());
Thread* self = Thread::Current();
- std::pair<std::set<mirror::ArtMethod*>::iterator, bool> pair;
{
WriterMutexLock mu(self, deoptimized_methods_lock_);
- pair = deoptimized_methods_.insert(method);
+ bool has_not_been_deoptimized = AddDeoptimizedMethod(method);
+ CHECK(has_not_been_deoptimized) << "Method " << PrettyMethod(method) << " is already deoptimized";
}
- bool already_deoptimized = !pair.second;
- CHECK(!already_deoptimized) << "Method " << PrettyMethod(method) << " is already deoptimized";
-
if (!interpreter_stubs_installed_) {
UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(),
false);
@@ -652,11 +709,10 @@ void Instrumentation::Undeoptimize(mirror::ArtMethod* method) {
bool empty;
{
WriterMutexLock mu(self, deoptimized_methods_lock_);
- auto it = deoptimized_methods_.find(method);
- CHECK(it != deoptimized_methods_.end()) << "Method " << PrettyMethod(method)
+ bool found_and_erased = RemoveDeoptimizedMethod(method);
+ CHECK(found_and_erased) << "Method " << PrettyMethod(method)
<< " is not deoptimized";
- deoptimized_methods_.erase(it);
- empty = deoptimized_methods_.empty();
+ empty = IsDeoptimizedMethodsEmpty();
}
// Restore code and possibly stack only if we did not deoptimize everything.
@@ -684,15 +740,15 @@ void Instrumentation::Undeoptimize(mirror::ArtMethod* method) {
}
}
-bool Instrumentation::IsDeoptimized(mirror::ArtMethod* method) const {
- ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
+bool Instrumentation::IsDeoptimized(mirror::ArtMethod* method) {
DCHECK(method != nullptr);
- return deoptimized_methods_.find(method) != deoptimized_methods_.end();
+ ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
+ return FindDeoptimizedMethod(method);
}
void Instrumentation::EnableDeoptimization() {
ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
- CHECK(deoptimized_methods_.empty());
+ CHECK(IsDeoptimizedMethodsEmpty());
CHECK_EQ(deoptimization_enabled_, false);
deoptimization_enabled_ = true;
}
@@ -708,10 +764,11 @@ void Instrumentation::DisableDeoptimization() {
mirror::ArtMethod* method;
{
ReaderMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
- if (deoptimized_methods_.empty()) {
+ if (IsDeoptimizedMethodsEmpty()) {
break;
}
- method = *deoptimized_methods_.begin();
+ method = BeginDeoptimizedMethod();
+ CHECK(method != nullptr);
}
Undeoptimize(method);
}
@@ -963,16 +1020,13 @@ void Instrumentation::PopMethodForUnwind(Thread* self, bool is_deoptimization) c
void Instrumentation::VisitRoots(RootCallback* callback, void* arg) {
WriterMutexLock mu(Thread::Current(), deoptimized_methods_lock_);
- if (deoptimized_methods_.empty()) {
+ if (IsDeoptimizedMethodsEmpty()) {
return;
}
- std::set<mirror::ArtMethod*> new_deoptimized_methods;
- for (mirror::ArtMethod* method : deoptimized_methods_) {
- DCHECK(method != nullptr);
- callback(reinterpret_cast<mirror::Object**>(&method), arg, 0, kRootVMInternal);
- new_deoptimized_methods.insert(method);
+ for (auto pair : deoptimized_methods_) {
+ mirror::ArtMethod** root = &pair.second;
+ callback(reinterpret_cast<mirror::Object**>(root), arg, 0, kRootVMInternal);
}
- deoptimized_methods_ = new_deoptimized_methods;
}
std::string InstrumentationStackFrame::Dump() const {
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index d0cb4ded04..cabb0e9c27 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -18,8 +18,8 @@
#define ART_RUNTIME_INSTRUMENTATION_H_
#include <stdint.h>
-#include <set>
#include <list>
+#include <map>
#include "atomic.h"
#include "instruction_set.h"
@@ -162,7 +162,9 @@ class Instrumentation {
LOCKS_EXCLUDED(Locks::thread_list_lock_, deoptimized_methods_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsDeoptimized(mirror::ArtMethod* method) const LOCKS_EXCLUDED(deoptimized_methods_lock_);
+ bool IsDeoptimized(mirror::ArtMethod* method)
+ LOCKS_EXCLUDED(deoptimized_methods_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Enable method tracing by installing instrumentation entry/exit stubs.
void EnableMethodTracing()
@@ -186,7 +188,7 @@ class Instrumentation {
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(mirror::ArtMethod* method, const void* quick_code,
- const void* portable_code, bool have_portable_code) const
+ const void* portable_code, bool have_portable_code)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Get the quick code for the given method. More efficient than asking the class linker as it
@@ -367,6 +369,23 @@ class Instrumentation {
mirror::ArtField* field, const JValue& field_value) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Read barrier-aware utility functions for accessing deoptimized_methods_
+ bool AddDeoptimizedMethod(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(deoptimized_methods_lock_);
+ bool FindDeoptimizedMethod(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(deoptimized_methods_lock_);
+ bool RemoveDeoptimizedMethod(mirror::ArtMethod* method)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ EXCLUSIVE_LOCKS_REQUIRED(deoptimized_methods_lock_);
+ mirror::ArtMethod* BeginDeoptimizedMethod()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(deoptimized_methods_lock_);
+ bool IsDeoptimizedMethodsEmpty() const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ SHARED_LOCKS_REQUIRED(deoptimized_methods_lock_);
+
// Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code?
bool instrumentation_stubs_installed_;
@@ -421,7 +440,7 @@ class Instrumentation {
// The set of methods being deoptimized (by the debugger) which must be executed with interpreter
// only.
mutable ReaderWriterMutex deoptimized_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::set<mirror::ArtMethod*> deoptimized_methods_ GUARDED_BY(deoptimized_methods_lock_);
+ std::multimap<int32_t, mirror::ArtMethod*> deoptimized_methods_ GUARDED_BY(deoptimized_methods_lock_);
bool deoptimization_enabled_;
// Current interpreter handler table. This is updated each time the thread state flags are
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index e3068b338d..47a7f0d62e 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -397,7 +397,8 @@ static inline JValue Execute(Thread* self, MethodHelper& mh, const DexFile::Code
void EnterInterpreterFromInvoke(Thread* self, ArtMethod* method, Object* receiver,
uint32_t* args, JValue* result) {
DCHECK_EQ(self, Thread::Current());
- if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return;
}
@@ -509,7 +510,8 @@ void EnterInterpreterFromDeoptimize(Thread* self, ShadowFrame* shadow_frame, JVa
JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame) {
DCHECK_EQ(self, Thread::Current());
- if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return JValue();
}
@@ -520,7 +522,8 @@ JValue EnterInterpreterFromStub(Thread* self, MethodHelper& mh, const DexFile::C
extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh,
const DexFile::CodeItem* code_item,
ShadowFrame* shadow_frame, JValue* result) {
- if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
+ bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
+ if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEndForInterpreter(implicit_check))) {
ThrowStackOverflowError(self);
return;
}
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 1bcd27e2d4..5a1d01e3f7 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -140,7 +140,8 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame,
return false;
}
const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- ArtMethod* const method = receiver->GetClass()->GetVTable()->GetWithoutChecks(vtable_idx);
+ CHECK(receiver->GetClass()->ShouldHaveEmbeddedImtAndVTable());
+ ArtMethod* const method = receiver->GetClass()->GetEmbeddedVTableEntry(vtable_idx);
if (UNLIKELY(method == nullptr)) {
CHECK(self->IsExceptionPending());
result->SetJ(0);
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index 325b089a48..8fd07cc11b 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -339,7 +339,7 @@ struct JdwpState {
ConditionVariable attach_cond_ GUARDED_BY(attach_lock_);
// Time of last debugger activity, in milliseconds.
- int64_t last_activity_time_ms_;
+ Atomic<int64_t> last_activity_time_ms_;
// Global counters and a mutex to protect them.
AtomicInteger request_serial_;
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 05bfe0ddf9..b9379f5e7f 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -1671,7 +1671,7 @@ size_t JdwpState::ProcessRequest(Request& request, ExpandBuf* pReply) {
* so waitForDebugger() doesn't return if we stall for a bit here.
*/
Dbg::GoActive();
- QuasiAtomic::Write64(&last_activity_time_ms_, 0);
+ last_activity_time_ms_.StoreSequentiallyConsistent(0);
}
/*
@@ -1751,7 +1751,7 @@ size_t JdwpState::ProcessRequest(Request& request, ExpandBuf* pReply) {
* the initial setup. Only update if this is a non-DDMS packet.
*/
if (request.GetCommandSet() != kJDWPDdmCmdSet) {
- QuasiAtomic::Write64(&last_activity_time_ms_, MilliTime());
+ last_activity_time_ms_.StoreSequentiallyConsistent(MilliTime());
}
/* tell the VM that GC is okay again */
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 64e9f370aa..7795b7c5df 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -577,7 +577,7 @@ int64_t JdwpState::LastDebuggerActivity() {
return -1;
}
- int64_t last = QuasiAtomic::Read64(&last_activity_time_ms_);
+ int64_t last = last_activity_time_ms_.LoadSequentiallyConsistent();
/* initializing or in the middle of something? */
if (last == 0) {
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index 167f848824..211ba1dde2 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -130,12 +130,11 @@ ArtMethod* ArtMethod::FindOverriddenMethod() {
Class* declaring_class = GetDeclaringClass();
Class* super_class = declaring_class->GetSuperClass();
uint16_t method_index = GetMethodIndex();
- ObjectArray<ArtMethod>* super_class_vtable = super_class->GetVTable();
ArtMethod* result = NULL;
// Did this method override a super class method? If so load the result from the super class'
// vtable
- if (super_class_vtable != NULL && method_index < super_class_vtable->GetLength()) {
- result = super_class_vtable->Get(method_index);
+ if (super_class->HasVTable() && method_index < super_class->GetVTableLength()) {
+ result = super_class->GetVTableEntry(method_index);
} else {
// Method didn't override superclass method so search interfaces
if (IsProxyMethod()) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 329a984842..c3754d7967 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -161,6 +161,37 @@ inline void Class::SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) {
CHECK(method == GetImTable()->Get(i));
}
+inline bool Class::HasVTable() {
+ return (GetVTable() != nullptr) || ShouldHaveEmbeddedImtAndVTable();
+}
+
+inline int32_t Class::GetVTableLength() {
+ if (ShouldHaveEmbeddedImtAndVTable()) {
+ return GetEmbeddedVTableLength();
+ }
+ return (GetVTable() != nullptr) ? GetVTable()->GetLength() : 0;
+}
+
+inline ArtMethod* Class::GetVTableEntry(uint32_t i) {
+ if (ShouldHaveEmbeddedImtAndVTable()) {
+ return GetEmbeddedVTableEntry(i);
+ }
+ return (GetVTable() != nullptr) ? GetVTable()->Get(i) : nullptr;
+}
+
+inline int32_t Class::GetEmbeddedVTableLength() {
+ return GetField32(EmbeddedVTableLengthOffset());
+}
+
+inline void Class::SetEmbeddedVTableLength(int32_t len) {
+ SetField32<false>(EmbeddedVTableLengthOffset(), len);
+}
+
+inline ArtMethod* Class::GetEmbeddedVTableEntry(uint32_t i) {
+ uint32_t offset = EmbeddedVTableOffset().Uint32Value() + i * sizeof(VTableEntry);
+ return GetFieldObject<mirror::ArtMethod>(MemberOffset(offset));
+}
+
inline void Class::SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) {
uint32_t offset = EmbeddedVTableOffset().Uint32Value() + i * sizeof(VTableEntry);
SetFieldObject<false>(MemberOffset(offset), method);
@@ -340,12 +371,12 @@ inline ArtMethod* Class::FindVirtualMethodForVirtual(ArtMethod* method) {
DCHECK(!method->GetDeclaringClass()->IsInterface() || method->IsMiranda());
// The argument method may from a super class.
// Use the index to a potentially overridden one for this instance's class.
- return GetVTable()->Get(method->GetMethodIndex());
+ return GetVTableEntry(method->GetMethodIndex());
}
inline ArtMethod* Class::FindVirtualMethodForSuper(ArtMethod* method) {
DCHECK(!method->GetDeclaringClass()->IsInterface());
- return GetSuperClass()->GetVTable()->Get(method->GetMethodIndex());
+ return GetSuperClass()->GetVTableEntry(method->GetMethodIndex());
}
inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* method) {
@@ -534,13 +565,19 @@ inline uint32_t Class::ComputeClassSize(bool has_embedded_tables,
if (has_embedded_tables) {
uint32_t embedded_imt_size = kImtSize * sizeof(ImTableEntry);
uint32_t embedded_vtable_size = num_vtable_entries * sizeof(VTableEntry);
- size += embedded_imt_size + embedded_vtable_size;
+ size += embedded_imt_size +
+ sizeof(int32_t) /* vtable len */ +
+ embedded_vtable_size;
}
// Space used by reference statics.
size += num_ref_static_fields * sizeof(HeapReference<Object>);
// Possible pad for alignment.
- if (((size & 7) != 0) && (num_64bit_static_fields > 0) && (num_32bit_static_fields == 0)) {
+ if (((size & 7) != 0) && (num_64bit_static_fields > 0)) {
size += sizeof(uint32_t);
+ if (num_32bit_static_fields != 0) {
+ // Shuffle one 32 bit static field forward.
+ num_32bit_static_fields--;
+ }
}
// Space used for primitive static fields.
size += (num_32bit_static_fields * sizeof(uint32_t)) +
@@ -574,7 +611,10 @@ inline void Class::VisitEmbeddedImtAndVTable(const Visitor& visitor) {
pos += sizeof(ImTableEntry);
}
- count = ((GetVTable() != NULL) ? GetVTable()->GetLength() : 0);
+ // Skip vtable length.
+ pos += sizeof(int32_t);
+
+ count = GetEmbeddedVTableLength();
for (size_t i = 0; i < count; ++i) {
MemberOffset offset = MemberOffset(pos);
visitor(this, offset, true);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index fadf80ebcf..a218b1cf5f 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -827,19 +827,56 @@ void Class::PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_
}
table = GetVTableDuringLinking();
- CHECK(table != nullptr);
+ CHECK(table != nullptr) << PrettyClass(this);
+ SetEmbeddedVTableLength(table->GetLength());
for (int32_t i = 0; i < table->GetLength(); i++) {
SetEmbeddedVTableEntry(i, table->Get(i));
}
+
+ SetImTable(nullptr);
+ // Keep java.lang.Object class's vtable around for since it's easier
+ // to be reused by array classes during their linking.
+ if (!IsObjectClass()) {
+ SetVTable(nullptr);
+ }
}
+// The pre-fence visitor for Class::CopyOf().
+class CopyClassVisitor {
+ public:
+ explicit CopyClassVisitor(Thread* self, Handle<mirror::Class>* orig,
+ size_t new_length, size_t copy_bytes)
+ : self_(self), orig_(orig), new_length_(new_length),
+ copy_bytes_(copy_bytes) {
+ }
+
+ void operator()(Object* obj, size_t usable_size) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ UNUSED(usable_size);
+ mirror::Class* new_class_obj = obj->AsClass();
+ mirror::Object::CopyObject(self_, new_class_obj, orig_->Get(), copy_bytes_);
+ new_class_obj->SetStatus(Class::kStatusResolving, self_);
+ new_class_obj->PopulateEmbeddedImtAndVTable();
+ new_class_obj->SetClassSize(new_length_);
+ }
+
+ private:
+ Thread* const self_;
+ Handle<mirror::Class>* const orig_;
+ const size_t new_length_;
+ const size_t copy_bytes_;
+ DISALLOW_COPY_AND_ASSIGN(CopyClassVisitor);
+};
+
Class* Class::CopyOf(Thread* self, int32_t new_length) {
DCHECK_GE(new_length, static_cast<int32_t>(sizeof(Class)));
// We may get copied by a compacting GC.
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_this(hs.NewHandle(this));
gc::Heap* heap = Runtime::Current()->GetHeap();
- InitializeClassVisitor visitor(new_length);
+ // The num_bytes (3rd param) is sizeof(Class) as opposed to SizeOf()
+ // to skip copying the tail part that we will overwrite here.
+ CopyClassVisitor visitor(self, &h_this, new_length, sizeof(Class));
mirror::Object* new_class =
kMovingClasses ? heap->AllocObject<true>(self, java_lang_Class_, new_length, visitor)
@@ -849,17 +886,7 @@ Class* Class::CopyOf(Thread* self, int32_t new_length) {
return NULL;
}
- mirror::Class* new_class_obj = new_class->AsClass();
- memcpy(new_class_obj, h_this.Get(), sizeof(Class));
-
- new_class_obj->SetStatus(kStatusResolving, self);
- new_class_obj->PopulateEmbeddedImtAndVTable();
- // Correct some fields.
- new_class_obj->SetLockWord(LockWord(), false);
- new_class_obj->SetClassSize(new_length);
-
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(new_class_obj);
- return new_class_obj;
+ return new_class->AsClass();
}
} // namespace mirror
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 648bddeca5..0525abf27a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -692,18 +692,34 @@ class MANAGED Class FINAL : public Object {
return MemberOffset(sizeof(Class));
}
- static MemberOffset EmbeddedVTableOffset() {
+ static MemberOffset EmbeddedVTableLengthOffset() {
return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry));
}
+ static MemberOffset EmbeddedVTableOffset() {
+ return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry) + sizeof(int32_t));
+ }
+
bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return IsInstantiable();
}
+ bool HasVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
ArtMethod* GetEmbeddedImTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ int32_t GetVTableLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* GetVTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ int32_t GetEmbeddedVTableLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void SetEmbeddedVTableLength(int32_t len) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ArtMethod* GetEmbeddedVTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
void SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
void PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 961bc64819..3543654868 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -65,8 +65,8 @@ class CopyReferenceFieldsWithReadBarrierVisitor {
Object* const dest_obj_;
};
-static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src, size_t num_bytes)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+Object* Object::CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src,
+ size_t num_bytes) {
// Copy instance data. We assume memcpy copies by words.
// TODO: expose and use move32.
byte* src_bytes = reinterpret_cast<byte*>(src);
@@ -107,7 +107,7 @@ class CopyObjectVisitor {
void operator()(Object* obj, size_t usable_size) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
UNUSED(usable_size);
- CopyObject(self_, obj, orig_->Get(), num_bytes_);
+ Object::CopyObject(self_, obj, orig_->Get(), num_bytes_);
}
private:
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 4fae4704bf..a6b622719e 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -370,6 +370,13 @@ class MANAGED LOCKABLE Object {
// Generate an identity hash code.
static int32_t GenerateIdentityHashCode();
+ // A utility function that copies an object in a read barrier and
+ // write barrier-aware way. This is internally used by Clone() and
+ // Class::CopyOf().
+ static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src,
+ size_t num_bytes)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// The Class representing the type of the object.
HeapReference<Class> klass_;
// Monitor and hash code information.
@@ -386,6 +393,8 @@ class MANAGED LOCKABLE Object {
friend class art::ImageWriter;
friend class art::Monitor;
friend struct art::ObjectOffsets; // for verifying offset information
+ friend class CopyObjectVisitor; // for CopyObject().
+ friend class CopyClassVisitor; // for CopyObject().
DISALLOW_IMPLICIT_CONSTRUCTORS(Object);
};
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 4b26edac61..aee30868ed 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -680,6 +680,8 @@ void Monitor::InflateThinLocked(Thread* self, Handle<mirror::Object> obj, LockWo
Thread* owner;
{
ScopedThreadStateChange tsc(self, kBlocked);
+ // Take suspend thread lock to avoid races with threads trying to suspend this one.
+ MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_);
owner = thread_list->SuspendThreadByThreadId(owner_thread_id, false, &timed_out);
}
if (owner != nullptr) {
@@ -745,10 +747,10 @@ mirror::Object* Monitor::MonitorEnter(Thread* self, mirror::Object* obj) {
contention_count++;
Runtime* runtime = Runtime::Current();
if (contention_count <= runtime->GetMaxSpinsBeforeThinkLockInflation()) {
- // TODO: Consider switch thread state to kBlocked when we are yielding.
+ // TODO: Consider switching the thread state to kBlocked when we are yielding.
// Use sched_yield instead of NanoSleep since NanoSleep can wait much longer than the
// parameter you pass in. This can cause thread suspension to take excessively long
- // make long pauses. See b/16307460.
+ // and make long pauses. See b/16307460.
sched_yield();
} else {
contention_count = 0;
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index cf310648e5..5f718ba213 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -35,7 +35,12 @@ static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject p
// Suspend thread to build stack trace.
soa.Self()->TransitionFromRunnableToSuspended(kNative);
bool timed_out;
- Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
+ Thread* thread;
+ {
+ // Take suspend thread lock to avoid races with threads trying to suspend this one.
+ MutexLock mu(soa.Self(), *Locks::thread_list_suspend_thread_lock_);
+ thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
+ }
if (thread != nullptr) {
// Must be runnable to create returned array.
CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kNative);
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index e577c2c960..124bdf5475 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -71,7 +71,10 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean
jthrowable cnfe = reinterpret_cast<jthrowable>(env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
WellKnownClasses::java_lang_ClassNotFoundException_init,
javaName, cause.get()));
- env->Throw(cnfe);
+ if (cnfe != nullptr) {
+ // Make sure allocation didn't fail with an OOME.
+ env->Throw(cnfe);
+ }
return nullptr;
}
if (initialize) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index bae67f20e8..8f83f96318 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -116,18 +116,25 @@ static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) {
static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) {
ScopedUtfChars name(env, java_name);
+ Thread* self;
{
ScopedObjectAccess soa(env);
if (soa.Decode<mirror::Object*>(peer) == soa.Self()->GetPeer()) {
soa.Self()->SetThreadName(name.c_str());
return;
}
+ self = soa.Self();
}
// Suspend thread to avoid it from killing itself while we set its name. We don't just hold the
// thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock
// in the DDMS send code.
bool timed_out;
- Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
+ // Take suspend thread lock to avoid races with threads trying to suspend this one.
+ Thread* thread;
+ {
+ MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_);
+ thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out);
+ }
if (thread != NULL) {
{
ScopedObjectAccess soa(env);
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index e17e60a7ce..45ef9ae727 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -61,7 +61,12 @@ 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;
+ {
+ // Take suspend thread lock to avoid races with threads trying to suspend this one.
+ MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_);
+ thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out);
+ }
if (thread != nullptr) {
{
ScopedObjectAccess soa(env);
diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h
index 65498deaab..e9ad3531b6 100644
--- a/runtime/noop_compiler_callbacks.h
+++ b/runtime/noop_compiler_callbacks.h
@@ -32,6 +32,11 @@ class NoopCompilerCallbacks FINAL : public CompilerCallbacks {
void ClassRejected(ClassReference ref) OVERRIDE {}
+ // This is only used by compilers which need to be able to run without relocation even when it
+ // would normally be enabled. For example the patchoat executable, and dex2oat --image, both need
+ // to disable the relocation since both deal with writing out the images directly.
+ bool IsRelocationPossible() OVERRIDE { return false; }
+
private:
DISALLOW_COPY_AND_ASSIGN(NoopCompilerCallbacks);
};
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 1421baffcf..0a8c35b561 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -23,7 +23,7 @@
namespace art {
const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '7', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' };
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
@@ -67,6 +67,8 @@ OatHeader::OatHeader(InstructionSet instruction_set,
const SafeMap<std::string, std::string>* variable_data) {
memcpy(magic_, kOatMagic, sizeof(kOatMagic));
memcpy(version_, kOatVersion, sizeof(kOatVersion));
+ executable_offset_ = 0;
+ image_patch_delta_ = 0;
adler32_checksum_ = adler32(0L, Z_NULL, 0);
@@ -98,7 +100,6 @@ OatHeader::OatHeader(InstructionSet instruction_set,
UpdateChecksum(&key_value_store_, key_value_store_size_);
}
- executable_offset_ = 0;
interpreter_to_interpreter_bridge_offset_ = 0;
interpreter_to_compiled_code_bridge_offset_ = 0;
jni_dlsym_lookup_offset_ = 0;
@@ -118,6 +119,12 @@ bool OatHeader::IsValid() const {
if (memcmp(version_, kOatVersion, sizeof(kOatVersion)) != 0) {
return false;
}
+ if (!IsAligned<kPageSize>(executable_offset_)) {
+ return false;
+ }
+ if (!IsAligned<kPageSize>(image_patch_delta_)) {
+ return false;
+ }
return true;
}
@@ -355,6 +362,26 @@ void OatHeader::SetQuickToInterpreterBridgeOffset(uint32_t offset) {
UpdateChecksum(&quick_to_interpreter_bridge_offset_, sizeof(offset));
}
+int32_t OatHeader::GetImagePatchDelta() const {
+ CHECK(IsValid());
+ return image_patch_delta_;
+}
+
+void OatHeader::RelocateOat(off_t delta) {
+ CHECK(IsValid());
+ CHECK_ALIGNED(delta, kPageSize);
+ image_patch_delta_ += delta;
+ if (image_file_location_oat_data_begin_ != 0) {
+ image_file_location_oat_data_begin_ += delta;
+ }
+}
+
+void OatHeader::SetImagePatchDelta(int32_t off) {
+ CHECK(IsValid());
+ CHECK_ALIGNED(off, kPageSize);
+ image_patch_delta_ = off;
+}
+
uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
CHECK(IsValid());
return image_file_location_oat_checksum_;
diff --git a/runtime/oat.h b/runtime/oat.h
index fbed596d33..6d5fefe2ce 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -88,6 +88,10 @@ class PACKED(4) OatHeader {
uint32_t GetQuickToInterpreterBridgeOffset() const;
void SetQuickToInterpreterBridgeOffset(uint32_t offset);
+ int32_t GetImagePatchDelta() const;
+ void RelocateOat(off_t delta);
+ void SetImagePatchDelta(int32_t off);
+
InstructionSet GetInstructionSet() const;
const InstructionSetFeatures& GetInstructionSetFeatures() const;
uint32_t GetImageFileLocationOatChecksum() const;
@@ -129,6 +133,9 @@ class PACKED(4) OatHeader {
uint32_t quick_resolution_trampoline_offset_;
uint32_t quick_to_interpreter_bridge_offset_;
+ // The amount that the image this oat is associated with has been patched.
+ int32_t image_patch_delta_;
+
uint32_t image_file_location_oat_checksum_;
uint32_t image_file_location_oat_data_begin_;
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 86c1baef27..f9cc36a0af 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -87,6 +87,11 @@ OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::str
return OpenElfFile(file, location, NULL, true, false, error_msg);
}
+OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
+ CheckLocation(location);
+ return OpenElfFile(file, location, NULL, false, false, error_msg);
+}
+
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
const std::string& location,
byte* requested_base,
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 44f4466724..3ec2e84345 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -52,6 +52,8 @@ class OatFile {
// ImageWriter which wants to open a writable version from an existing
// file descriptor for patching.
static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
+ // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
+ static OatFile* OpenReadable(File* file, const std::string& location, std::string* error_msg);
// Open an oat file backed by a std::vector with the given location.
static OatFile* OpenMemory(std::vector<uint8_t>& oat_contents,
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 9a1d0f71aa..8d0aff802b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -221,6 +221,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
compiler_callbacks_ = nullptr;
is_zygote_ = false;
+ must_relocate_ = kDefaultMustRelocate;
if (kPoisonHeapReferences) {
// kPoisonHeapReferences currently works only with the interpreter only.
// TODO: make it work with the compiler.
@@ -390,6 +391,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
ignore_max_footprint_ = true;
} else if (option == "-XX:LowMemoryMode") {
low_memory_mode_ = true;
+ // TODO Might want to turn off must_relocate here.
} else if (option == "-XX:UseTLAB") {
use_tlab_ = true;
} else if (option == "-XX:EnableHSpaceCompactForOOM") {
@@ -408,6 +410,14 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize
reinterpret_cast<const char*>(options[i].second));
} else if (option == "-Xzygote") {
is_zygote_ = true;
+ } else if (StartsWith(option, "-Xpatchoat:")) {
+ if (!ParseStringAfterChar(option, ':', &patchoat_executable_)) {
+ return false;
+ }
+ } else if (option == "-Xrelocate") {
+ must_relocate_ = true;
+ } else if (option == "-Xnorelocate") {
+ must_relocate_ = false;
} else if (option == "-Xint") {
interpreter_only_ = true;
} else if (StartsWith(option, "-Xgc:")) {
@@ -758,6 +768,8 @@ void ParsedOptions::Usage(const char* fmt, ...) {
UsageMessage(stream, " -Xcompiler:filename\n");
UsageMessage(stream, " -Xcompiler-option dex2oat-option\n");
UsageMessage(stream, " -Ximage-compiler-option dex2oat-option\n");
+ UsageMessage(stream, " -Xpatchoat:filename\n");
+ UsageMessage(stream, " -X[no]relocate\n");
UsageMessage(stream, "\n");
UsageMessage(stream, "The following previously supported Dalvik options are ignored:\n");
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 23f2bcfa62..29d54945a6 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -47,6 +47,10 @@ class ParsedOptions {
std::string jni_trace_;
CompilerCallbacks* compiler_callbacks_;
bool is_zygote_;
+ // TODO Change this to true when we want it on by default.
+ static constexpr bool kDefaultMustRelocate = false;
+ bool must_relocate_;
+ std::string patchoat_executable_;
bool interpreter_only_;
bool is_explicit_gc_disabled_;
bool use_tlab_;
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index d2877f98d1..db98e1fc9d 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -17,7 +17,7 @@
#include "reference_table.h"
#include "common_runtime_test.h"
-#include "mirror/array.h"
+#include "mirror/array-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index aca2607bfc..b7eae850bf 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -101,6 +101,7 @@ Runtime::Runtime()
instruction_set_(kNone),
compiler_callbacks_(nullptr),
is_zygote_(false),
+ must_relocate_(false),
is_concurrent_gc_enabled_(true),
is_explicit_gc_disabled_(false),
default_stack_size_(0),
@@ -385,6 +386,15 @@ jobject CreateSystemClassLoader() {
return env->NewGlobalRef(system_class_loader.get());
}
+std::string Runtime::GetPatchoatExecutable() const {
+ if (!patchoat_executable_.empty()) {
+ return patchoat_executable_;
+ }
+ std::string patchoat_executable_(GetAndroidRoot());
+ patchoat_executable_ += (kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat");
+ return patchoat_executable_;
+}
+
std::string Runtime::GetCompilerExecutable() const {
if (!compiler_executable_.empty()) {
return compiler_executable_;
@@ -557,6 +567,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
properties_ = options->properties_;
compiler_callbacks_ = options->compiler_callbacks_;
+ patchoat_executable_ = options->patchoat_executable_;
+ must_relocate_ = options->must_relocate_;
is_zygote_ = options->is_zygote_;
is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
@@ -629,8 +641,7 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
break;
}
- if (!options->interpreter_only_ &&
- (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_)) {
+ if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {
fault_manager.Init();
// These need to be in a specific order. The null point check handler must be
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 284e4ffe30..c8e462e699 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -21,10 +21,12 @@
#include <stdio.h>
#include <iosfwd>
+#include <set>
#include <string>
#include <utility>
#include <vector>
+#include "compiler_callbacks.h"
#include "instrumentation.h"
#include "instruction_set.h"
#include "jobject_comparator.h"
@@ -54,7 +56,6 @@ namespace verifier {
class MethodVerifier;
}
class ClassLinker;
-class CompilerCallbacks;
class DexFile;
class InternTable;
class JavaVMExt;
@@ -91,6 +92,18 @@ class Runtime {
return compiler_callbacks_ != nullptr;
}
+ bool CanRelocate() const {
+ return !IsCompiler() || compiler_callbacks_->IsRelocationPossible();
+ }
+
+ bool ShouldRelocate() const {
+ return must_relocate_ && CanRelocate();
+ }
+
+ bool MustRelocateIfPossible() const {
+ return must_relocate_;
+ }
+
CompilerCallbacks* GetCompilerCallbacks() {
return compiler_callbacks_;
}
@@ -104,6 +117,7 @@ class Runtime {
}
std::string GetCompilerExecutable() const;
+ std::string GetPatchoatExecutable() const;
const std::vector<std::string>& GetCompilerOptions() const {
return compiler_options_;
@@ -173,7 +187,7 @@ class Runtime {
void DetachCurrentThread() LOCKS_EXCLUDED(Locks::mutator_lock_);
void DumpForSigQuit(std::ostream& os)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_);
void DumpLockHolders(std::ostream& os);
~Runtime();
@@ -485,10 +499,12 @@ class Runtime {
CompilerCallbacks* compiler_callbacks_;
bool is_zygote_;
+ bool must_relocate_;
bool is_concurrent_gc_enabled_;
bool is_explicit_gc_disabled_;
std::string compiler_executable_;
+ std::string patchoat_executable_;
std::vector<std::string> compiler_options_;
std::vector<std::string> image_compiler_options_;
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 38f1307321..a5caa075ee 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -57,26 +57,24 @@ inline ThreadState Thread::SetState(ThreadState new_state) {
}
inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const {
-#ifdef NDEBUG
- UNUSED(check_locks); // Keep GCC happy about unused parameters.
-#else
- CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause;
- if (check_locks) {
- bool bad_mutexes_held = false;
- for (int i = kLockLevelCount - 1; i >= 0; --i) {
- // We expect no locks except the mutator_lock_.
- if (i != kMutatorLock) {
- BaseMutex* held_mutex = GetHeldMutex(static_cast<LockLevel>(i));
- if (held_mutex != NULL) {
- LOG(ERROR) << "holding \"" << held_mutex->GetName()
- << "\" at point where thread suspension is expected";
- bad_mutexes_held = true;
+ if (kIsDebugBuild) {
+ CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause;
+ if (check_locks) {
+ bool bad_mutexes_held = false;
+ for (int i = kLockLevelCount - 1; i >= 0; --i) {
+ // We expect no locks except the mutator_lock_ or thread list suspend thread lock.
+ if (i != kMutatorLock && i != kThreadListSuspendThreadLock) {
+ BaseMutex* held_mutex = GetHeldMutex(static_cast<LockLevel>(i));
+ if (held_mutex != NULL) {
+ LOG(ERROR) << "holding \"" << held_mutex->GetName()
+ << "\" at point where thread suspension is expected";
+ bad_mutexes_held = true;
+ }
}
}
+ CHECK(!bad_mutexes_held);
}
- CHECK(!bad_mutexes_held);
}
-#endif
}
inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index f888029af1..8151464ccf 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -76,6 +76,8 @@ namespace art {
bool Thread::is_started_ = false;
pthread_key_t Thread::pthread_key_self_;
ConditionVariable* Thread::resume_cond_ = nullptr;
+const size_t Thread::kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
+ GetStackOverflowReservedBytes(kRuntimeISA);
static const char* kThreadNameDuringStartup = "<native thread without managed peer>";
@@ -219,7 +221,7 @@ static size_t FixStackSize(size_t stack_size) {
// It's likely that callers are trying to ensure they have at least a certain amount of
// stack space, so we should add our reserved space on top of what they requested, rather
// than implicitly take it away from them.
- stack_size += kRuntimeStackOverflowReservedBytes;
+ stack_size += GetStackOverflowReservedBytes(kRuntimeISA);
} else {
// If we are going to use implicit stack checks, allocate space for the protected
// region at the bottom of the stack.
@@ -308,7 +310,7 @@ void Thread::InstallImplicitProtection(bool is_main_stack) {
if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) {
LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:"
- << strerror(errno);
+ << strerror(errno) << kStackOverflowProtectedSize;
}
// Tell the kernel that we won't be needing these pages any more.
@@ -536,7 +538,7 @@ void Thread::InitStackHwm() {
tlsPtr_.stack_begin = reinterpret_cast<byte*>(read_stack_base);
tlsPtr_.stack_size = read_stack_size;
- if (read_stack_size <= kRuntimeStackOverflowReservedBytes) {
+ if (read_stack_size <= GetStackOverflowReservedBytes(kRuntimeISA)) {
LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size
<< " bytes)";
}
@@ -1138,7 +1140,7 @@ void Thread::AssertNoPendingExceptionForNewException(const char* msg) const {
if (UNLIKELY(IsExceptionPending())) {
ScopedObjectAccess soa(Thread::Current());
mirror::Throwable* exception = GetException(nullptr);
- LOG(FATAL) << "Throwing new exception " << msg << " with unexpected pending exception: "
+ LOG(FATAL) << "Throwing new exception '" << msg << "' with unexpected pending exception: "
<< exception->Dump();
}
}
@@ -2247,7 +2249,7 @@ void Thread::SetStackEndForStackOverflow() {
if (tlsPtr_.stack_end == tlsPtr_.stack_begin) {
// However, we seem to have already extended to use the full stack.
LOG(ERROR) << "Need to increase kStackOverflowReservedBytes (currently "
- << kRuntimeStackOverflowReservedBytes << ")?";
+ << GetStackOverflowReservedBytes(kRuntimeISA) << ")?";
DumpStack(LOG(ERROR));
LOG(FATAL) << "Recursive stack overflow.";
}
diff --git a/runtime/thread.h b/runtime/thread.h
index d08c2fce82..998f7db460 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -104,8 +104,7 @@ class Thread {
// is protected against reads and the lower is available for use while
// throwing the StackOverflow exception.
static constexpr size_t kStackOverflowProtectedSize = 16 * KB;
- static constexpr size_t kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize +
- kRuntimeStackOverflowReservedBytes;
+ static const size_t kStackOverflowImplicitCheckSize;
// Creates a new native thread corresponding to the given managed peer.
// Used to implement Thread.start.
@@ -551,6 +550,16 @@ class Thread {
return tlsPtr_.stack_size - (tlsPtr_.stack_end - tlsPtr_.stack_begin);
}
+ byte* GetStackEndForInterpreter(bool implicit_overflow_check) const {
+ if (implicit_overflow_check) {
+ // The interpreter needs the extra overflow bytes that stack_end does
+ // not include.
+ return tlsPtr_.stack_end + GetStackOverflowReservedBytes(kRuntimeISA);
+ } else {
+ return tlsPtr_.stack_end;
+ }
+ }
+
byte* GetStackEnd() const {
return tlsPtr_.stack_end;
}
@@ -567,7 +576,7 @@ class Thread {
// overflow region.
tlsPtr_.stack_end = tlsPtr_.stack_begin + kStackOverflowImplicitCheckSize;
} else {
- tlsPtr_.stack_end = tlsPtr_.stack_begin + kRuntimeStackOverflowReservedBytes;
+ tlsPtr_.stack_end = tlsPtr_.stack_begin + GetStackOverflowReservedBytes(kRuntimeISA);
}
}
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index b649b626ca..ff1a07974a 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -170,16 +170,7 @@ static void UnsafeLogFatalForThreadSuspendAllTimeout() {
// individual thread requires polling. delay_us is the requested sleep and total_delay_us
// accumulates the total time spent sleeping for timeouts. The first sleep is just a yield,
// subsequently sleeps increase delay_us from 1ms to 500ms by doubling.
-static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us,
- bool holding_locks) {
- if (!holding_locks) {
- for (int i = kLockLevelCount - 1; i >= 0; --i) {
- BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i));
- if (held_mutex != NULL) {
- LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension";
- }
- }
- }
+static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) {
useconds_t new_delay_us = (*delay_us) * 2;
CHECK_GE(new_delay_us, *delay_us);
if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s.
@@ -244,7 +235,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) {
useconds_t total_delay_us = 0;
do {
useconds_t delay_us = 100;
- ThreadSuspendSleep(self, &delay_us, &total_delay_us, true);
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
} while (!thread->IsSuspended());
// Shouldn't need to wait for longer than 1000 microseconds.
constexpr useconds_t kLongWaitThresholdUS = 1000;
@@ -444,6 +435,11 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
while (true) {
Thread* thread;
{
+ // Note: this will transition to runnable and potentially suspend. We ensure only one thread
+ // is requesting another suspend, to avoid deadlock, by requiring this function be called
+ // holding Locks::thread_list_suspend_thread_lock_. Its important this thread suspend rather
+ // than request thread suspension, to avoid potential cycles in threads requesting each other
+ // suspend.
ScopedObjectAccess soa(self);
MutexLock mu(self, *Locks::thread_list_lock_);
thread = Thread::FromManagedThread(soa, peer);
@@ -483,7 +479,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension,
}
// Release locks and come out of runnable state.
}
- ThreadSuspendSleep(self, &delay_us, &total_delay_us, false);
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
}
}
@@ -502,9 +498,14 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe
CHECK_NE(thread_id, kInvalidThreadId);
while (true) {
{
- Thread* thread = NULL;
+ // Note: this will transition to runnable and potentially suspend. We ensure only one thread
+ // is requesting another suspend, to avoid deadlock, by requiring this function be called
+ // holding Locks::thread_list_suspend_thread_lock_. Its important this thread suspend rather
+ // than request thread suspension, to avoid potential cycles in threads requesting each other
+ // suspend.
ScopedObjectAccess soa(self);
MutexLock mu(self, *Locks::thread_list_lock_);
+ Thread* thread = nullptr;
for (const auto& it : list_) {
if (it->GetThreadId() == thread_id) {
thread = it;
@@ -550,7 +551,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe
}
// Release locks and come out of runnable state.
}
- ThreadSuspendSleep(self, &delay_us, &total_delay_us, false);
+ ThreadSuspendSleep(self, &delay_us, &total_delay_us);
}
}
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index d46987a8b8..1b67ac0588 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -68,6 +68,7 @@ class ThreadList {
// is set to true.
static Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension,
bool* timed_out)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_suspend_thread_lock_)
LOCKS_EXCLUDED(Locks::mutator_lock_,
Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
@@ -77,6 +78,7 @@ class ThreadList {
// the thread terminating. Note that as thread ids are recycled this may not suspend the expected
// thread, that may be terminating. If the suspension times out then *timeout is set to true.
Thread* SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, bool* timed_out)
+ EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_suspend_thread_lock_)
LOCKS_EXCLUDED(Locks::mutator_lock_,
Locks::thread_list_lock_,
Locks::thread_suspend_count_lock_);
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 8b1ad39edc..52cdcc1d6c 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1154,22 +1154,55 @@ const char* GetAndroidRoot() {
}
const char* GetAndroidData() {
+ std::string error_msg;
+ const char* dir = GetAndroidDataSafe(&error_msg);
+ if (dir != nullptr) {
+ return dir;
+ } else {
+ LOG(FATAL) << error_msg;
+ return "";
+ }
+}
+
+const char* GetAndroidDataSafe(std::string* error_msg) {
const char* android_data = getenv("ANDROID_DATA");
if (android_data == NULL) {
if (OS::DirectoryExists("/data")) {
android_data = "/data";
} else {
- LOG(FATAL) << "ANDROID_DATA not set and /data does not exist";
- return "";
+ *error_msg = "ANDROID_DATA not set and /data does not exist";
+ return nullptr;
}
}
if (!OS::DirectoryExists(android_data)) {
- LOG(FATAL) << "Failed to find ANDROID_DATA directory " << android_data;
- return "";
+ *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data);
+ return nullptr;
}
return android_data;
}
+void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
+ bool* have_android_data, bool* dalvik_cache_exists) {
+ CHECK(subdir != nullptr);
+ std::string error_msg;
+ const char* android_data = GetAndroidDataSafe(&error_msg);
+ if (android_data == nullptr) {
+ *have_android_data = false;
+ *dalvik_cache_exists = false;
+ return;
+ } else {
+ *have_android_data = true;
+ }
+ const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
+ *dalvik_cache = dalvik_cache_root + subdir;
+ *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str());
+ if (create_if_absent && !*dalvik_cache_exists && strcmp(android_data, "/data") != 0) {
+ // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
+ *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) &&
+ (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST));
+ }
+}
+
std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) {
CHECK(subdir != nullptr);
const char* android_data = GetAndroidData();
@@ -1196,9 +1229,11 @@ std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent)
return dalvik_cache;
}
-std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) {
+bool GetDalvikCacheFilename(const char* location, const char* cache_location,
+ std::string* filename, std::string* error_msg) {
if (location[0] != '/') {
- LOG(FATAL) << "Expected path in location to be absolute: "<< location;
+ *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
+ return false;
}
std::string cache_file(&location[1]); // skip leading slash
if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) {
@@ -1206,7 +1241,17 @@ std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_
cache_file += DexFile::kClassesDex;
}
std::replace(cache_file.begin(), cache_file.end(), '/', '@');
- return StringPrintf("%s/%s", cache_location, cache_file.c_str());
+ *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
+ return true;
+}
+
+std::string GetDalvikCacheFilenameOrDie(const char* location, const char* cache_location) {
+ std::string ret;
+ std::string error_msg;
+ if (!GetDalvikCacheFilename(location, cache_location, &ret, &error_msg)) {
+ LOG(FATAL) << error_msg;
+ }
+ return ret;
}
static void InsertIsaDirectory(const InstructionSet isa, std::string* filename) {
diff --git a/runtime/utils.h b/runtime/utils.h
index c920050234..73872d31d9 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -439,11 +439,22 @@ const char* GetAndroidRoot();
// Find $ANDROID_DATA, /data, or abort.
const char* GetAndroidData();
+// Find $ANDROID_DATA, /data, or return nullptr.
+const char* GetAndroidDataSafe(std::string* error_msg);
// Returns the dalvik-cache location, or dies trying. subdir will be
// appended to the cache location.
std::string GetDalvikCacheOrDie(const char* subdir, bool create_if_absent = true);
-
+// Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
+// have_android_data will be set to true if we have an ANDROID_DATA that exists,
+// dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
+void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache,
+ bool* have_android_data, bool* dalvik_cache_exists);
+
+// Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be
+// rooted at cache_location.
+bool GetDalvikCacheFilename(const char* file_location, const char* cache_location,
+ std::string* filename, std::string* error_msg);
// Returns the absolute dalvik-cache path for a DexFile or OatFile, or
// dies trying. The path returned will be rooted at cache_location.
std::string GetDalvikCacheFilenameOrDie(const char* file_location,
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index f1b5afd235..de792cbd58 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1728,6 +1728,15 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
const uint32_t type_idx = (is_checkcast) ? inst->VRegB_21c() : inst->VRegC_22c();
const RegType& res_type = ResolveClassAndCheckAccess(type_idx);
if (res_type.IsConflict()) {
+ // If this is a primitive type, fail HARD.
+ mirror::Class* klass = (*dex_cache_)->GetResolvedType(type_idx);
+ if (klass != nullptr && klass->IsPrimitive()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type "
+ << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in "
+ << GetDeclaringClass();
+ break;
+ }
+
DCHECK_NE(failures_.size(), 0U);
if (!is_checkcast) {
work_line_->SetRegisterType(inst->VRegA_22c(), reg_types_.Boolean());
@@ -1972,6 +1981,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
if (!orig_type.Equals(cast_type) &&
!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
+ cast_type.HasClass() && // Could be conflict type, make sure it has a class.
!cast_type.GetClass()->IsInterface() &&
(orig_type.IsZero() ||
orig_type.IsStrictlyAssignableFrom(cast_type.Merge(orig_type, &reg_types_)))) {
@@ -2763,12 +2773,30 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
* "try" block when they throw, control transfers out of the method.)
*/
if ((opcode_flags & Instruction::kThrow) != 0 && insn_flags_[work_insn_idx_].IsInTry()) {
- bool within_catch_all = false;
+ bool has_catch_all_handler = false;
CatchHandlerIterator iterator(*code_item_, work_insn_idx_);
+ // Need the linker to try and resolve the handled class to check if it's Throwable.
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+
for (; iterator.HasNext(); iterator.Next()) {
- if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) {
- within_catch_all = true;
+ uint16_t handler_type_idx = iterator.GetHandlerTypeIndex();
+ if (handler_type_idx == DexFile::kDexNoIndex16) {
+ has_catch_all_handler = true;
+ } else {
+ // It is also a catch-all if it is java.lang.Throwable.
+ mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, *dex_cache_,
+ *class_loader_);
+ if (klass != nullptr) {
+ if (klass == mirror::Throwable::GetJavaLangThrowable()) {
+ has_catch_all_handler = true;
+ }
+ } else {
+ // Clear exception.
+ Thread* self = Thread::Current();
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ }
}
/*
* Merge registers into the "catch" block. We want to use the "savedRegs" rather than
@@ -2784,7 +2812,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
* If the monitor stack depth is nonzero, there must be a "catch all" handler for this
* instruction. This does apply to monitor-exit because of async exception handling.
*/
- if (work_line_->MonitorStackDepth() > 0 && !within_catch_all) {
+ if (work_line_->MonitorStackDepth() > 0 && !has_catch_all_handler) {
/*
* The state in work_line reflects the post-execution state. If the current instruction is a
* monitor-enter and the monitor stack was empty, we don't need a catch-all (if it throws,
@@ -3225,7 +3253,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst,
return nullptr;
}
mirror::Class* super_klass = super.GetClass();
- if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
+ if (res_method->GetMethodIndex() >= super_klass->GetVTableLength()) {
Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
<< PrettyMethod(dex_method_idx_, *dex_file_)
<< " to super " << super
@@ -3250,20 +3278,21 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst
VLOG(verifier) << "Failed to get mirror::Class* from '" << actual_arg_type << "'";
return nullptr;
}
- mirror::ObjectArray<mirror::ArtMethod>* vtable = nullptr;
mirror::Class* klass = actual_arg_type.GetClass();
+ mirror::Class* dispatch_class;
if (klass->IsInterface()) {
// Derive Object.class from Class.class.getSuperclass().
mirror::Class* object_klass = klass->GetClass()->GetSuperClass();
CHECK(object_klass->IsObjectClass());
- vtable = object_klass->GetVTable();
+ dispatch_class = object_klass;
} else {
- vtable = klass->GetVTable();
+ dispatch_class = klass;
}
- CHECK(vtable != nullptr) << PrettyDescriptor(klass);
+ CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class);
uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
- CHECK_LT(static_cast<int32_t>(vtable_index), vtable->GetLength()) << PrettyDescriptor(klass);
- mirror::ArtMethod* res_method = vtable->Get(vtable_index);
+ CHECK_LT(static_cast<int32_t>(vtable_index), dispatch_class->GetVTableLength())
+ << PrettyDescriptor(klass);
+ mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index);
CHECK(!Thread::Current()->IsExceptionPending());
return res_method;
}
@@ -3469,10 +3498,12 @@ void MethodVerifier::VerifyPrimitivePut(const RegType& target_type, const RegTyp
value_compatible = value_type.IsFloatTypes();
} else if (target_type.IsLong()) {
instruction_compatible = insn_type.IsLong();
- value_compatible = value_type.IsLongTypes();
+ const RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+ value_compatible = value_type.IsLongTypes() && value_type.CheckWidePair(value_type_hi);
} else if (target_type.IsDouble()) {
instruction_compatible = insn_type.IsLong(); // no put-double, so expect put-long
- value_compatible = value_type.IsDoubleTypes();
+ const RegType& value_type_hi = work_line_->GetRegisterType(vregA + 1);
+ value_compatible = value_type.IsDoubleTypes() && value_type.CheckWidePair(value_type_hi);
} else {
instruction_compatible = false; // reference with primitive store
value_compatible = false; // unused
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index fdc6e3f487..3a6a72b841 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -47,6 +47,8 @@ jclass WellKnownClasses::java_lang_Thread$UncaughtExceptionHandler;
jclass WellKnownClasses::java_lang_ThreadGroup;
jclass WellKnownClasses::java_lang_Throwable;
jclass WellKnownClasses::java_nio_DirectByteBuffer;
+jclass WellKnownClasses::java_util_Collections;
+jclass WellKnownClasses::libcore_util_EmptyArray;
jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
@@ -150,6 +152,8 @@ void WellKnownClasses::Init(JNIEnv* env) {
java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
+ java_util_Collections = CacheClass(env, "java/util/Collections");
+ libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index f6c2930909..7639f50c69 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -60,7 +60,9 @@ struct WellKnownClasses {
static jclass java_lang_ThreadGroup;
static jclass java_lang_Thread$UncaughtExceptionHandler;
static jclass java_lang_Throwable;
+ static jclass java_util_Collections;
static jclass java_nio_DirectByteBuffer;
+ static jclass libcore_util_EmptyArray;
static jclass org_apache_harmony_dalvik_ddmc_Chunk;
static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
diff --git a/test/001-nop/build b/test/000-nop/build
index 5233a2d716..5233a2d716 100644
--- a/test/001-nop/build
+++ b/test/000-nop/build
diff --git a/test/001-nop/expected.txt b/test/000-nop/expected.txt
index 80a233e745..80a233e745 100644
--- a/test/001-nop/expected.txt
+++ b/test/000-nop/expected.txt
diff --git a/test/001-nop/info.txt b/test/000-nop/info.txt
index 9942f10dad..9942f10dad 100644
--- a/test/001-nop/info.txt
+++ b/test/000-nop/info.txt
diff --git a/test/001-nop/run b/test/000-nop/run
index 210296b57c..210296b57c 100644
--- a/test/001-nop/run
+++ b/test/000-nop/run
diff --git a/test/001-HelloWorld/expected.txt b/test/001-HelloWorld/expected.txt
new file mode 100644
index 0000000000..af5626b4a1
--- /dev/null
+++ b/test/001-HelloWorld/expected.txt
@@ -0,0 +1 @@
+Hello, world!
diff --git a/test/001-HelloWorld/info.txt b/test/001-HelloWorld/info.txt
new file mode 100644
index 0000000000..641dd9aa24
--- /dev/null
+++ b/test/001-HelloWorld/info.txt
@@ -0,0 +1 @@
+Imported from oat test. Print "Hello World."
diff --git a/test/HelloWorld/HelloWorld.java b/test/001-HelloWorld/src/Main.java
index c6861ced7a..1ef6289559 100644
--- a/test/HelloWorld/HelloWorld.java
+++ b/test/001-HelloWorld/src/Main.java
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-class HelloWorld {
+public class Main {
public static void main(String[] args) {
- System.logI("Hello, world!");
+ System.out.println("Hello, world!");
}
}
diff --git a/test/001-Main/expected.txt b/test/001-Main/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/001-Main/expected.txt
diff --git a/test/001-Main/info.txt b/test/001-Main/info.txt
new file mode 100644
index 0000000000..e4b33a052d
--- /dev/null
+++ b/test/001-Main/info.txt
@@ -0,0 +1 @@
+Import of a previous oat test. Empty main, just test starting up the runtime.
diff --git a/test/009-instanceof2/src/Iface2.java b/test/001-Main/src/Main.java
index 2b33c39ab3..3486866a2a 100644
--- a/test/009-instanceof2/src/Iface2.java
+++ b/test/001-Main/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-/**
- * Another interface.
- */
-public interface Iface2 {
-
- public int iFunc2(int ii);
+public class Main {
+ public static void main(String args[]) {
+ }
}
diff --git a/test/004-InterfaceTest/expected.txt b/test/004-InterfaceTest/expected.txt
new file mode 100644
index 0000000000..4854e24149
--- /dev/null
+++ b/test/004-InterfaceTest/expected.txt
@@ -0,0 +1,2 @@
+test_virtual done
+test_interface done
diff --git a/test/004-InterfaceTest/info.txt b/test/004-InterfaceTest/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-InterfaceTest/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/InterfaceTest/InterfaceTest.java b/test/004-InterfaceTest/src/Main.java
index ed18eb3dc7..9ebac59445 100644
--- a/test/InterfaceTest/InterfaceTest.java
+++ b/test/004-InterfaceTest/src/Main.java
@@ -17,7 +17,7 @@
import java.util.Map;
import java.util.HashMap;
-class InterfaceTest {
+public class Main {
public static long test_virtual(HashMap map) {
Integer intobj = new Integer(0);
@@ -44,10 +44,10 @@ class InterfaceTest {
public static void main(String[] args) {
HashMap hashmap = new HashMap();
long elapsed = test_virtual(hashmap);
- System.logI("virtual map put: " + elapsed);
+ System.out.println("test_virtual done");
hashmap.clear();
elapsed = test_interface(hashmap);
- System.logI("interface map put: " + elapsed);
+ System.out.println("test_interface done");
}
}
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/004-JniTest/expected.txt
diff --git a/test/004-JniTest/info.txt b/test/004-JniTest/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-JniTest/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index 36cad72ace..4909a4a77c 100644
--- a/test/JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -42,7 +42,7 @@ static void* testFindClassOnAttachedNativeThread(void*) {
int attach_result = jvm->AttachCurrentThread(&env, &args);
assert(attach_result == 0);
- jclass clazz = env->FindClass("JniTest");
+ jclass clazz = env->FindClass("Main");
assert(clazz != NULL);
assert(!env->ExceptionCheck());
@@ -56,7 +56,7 @@ static void* testFindClassOnAttachedNativeThread(void*) {
}
// http://b/10994325
-extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindClassOnAttachedNativeThread(JNIEnv*,
+extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*,
jclass) {
pthread_t pthread;
int pthread_create_result = pthread_create(&pthread,
@@ -76,7 +76,7 @@ static void* testFindFieldOnAttachedNativeThread(void*) {
int attach_result = jvm->AttachCurrentThread(&env, &args);
assert(attach_result == 0);
- jclass clazz = env->FindClass("JniTest");
+ jclass clazz = env->FindClass("Main");
assert(clazz != NULL);
assert(!env->ExceptionCheck());
@@ -91,7 +91,7 @@ static void* testFindFieldOnAttachedNativeThread(void*) {
return NULL;
}
-extern "C" JNIEXPORT void JNICALL Java_JniTest_testFindFieldOnAttachedNativeThreadNative(JNIEnv*,
+extern "C" JNIEXPORT void JNICALL Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv*,
jclass) {
pthread_t pthread;
int pthread_create_result = pthread_create(&pthread,
@@ -111,7 +111,7 @@ static void* testReflectFieldGetFromAttachedNativeThread(void*) {
int attach_result = jvm->AttachCurrentThread(&env, &args);
assert(attach_result == 0);
- jclass clazz = env->FindClass("JniTest");
+ jclass clazz = env->FindClass("Main");
assert(clazz != NULL);
assert(!env->ExceptionCheck());
@@ -151,7 +151,7 @@ static void* testReflectFieldGetFromAttachedNativeThread(void*) {
}
// http://b/15539150
-extern "C" JNIEXPORT void JNICALL Java_JniTest_testReflectFieldGetFromAttachedNativeThreadNative(
+extern "C" JNIEXPORT void JNICALL Java_Main_testReflectFieldGetFromAttachedNativeThreadNative(
JNIEnv*, jclass) {
pthread_t pthread;
int pthread_create_result = pthread_create(&pthread,
@@ -165,22 +165,22 @@ extern "C" JNIEXPORT void JNICALL Java_JniTest_testReflectFieldGetFromAttachedNa
// http://b/11243757
-extern "C" JNIEXPORT void JNICALL Java_JniTest_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
+extern "C" JNIEXPORT void JNICALL Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
jclass) {
- jclass super_class = env->FindClass("JniTest$testCallStaticVoidMethodOnSubClass_SuperClass");
+ jclass super_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SuperClass");
assert(super_class != NULL);
jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V");
assert(execute != NULL);
- jclass sub_class = env->FindClass("JniTest$testCallStaticVoidMethodOnSubClass_SubClass");
+ jclass sub_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SubClass");
assert(sub_class != NULL);
env->CallStaticVoidMethod(sub_class, execute);
}
-extern "C" JNIEXPORT jobject JNICALL Java_JniTest_testGetMirandaMethodNative(JNIEnv* env, jclass) {
- jclass abstract_class = env->FindClass("JniTest$testGetMirandaMethod_MirandaAbstract");
+extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass) {
+ jclass abstract_class = env->FindClass("Main$testGetMirandaMethod_MirandaAbstract");
assert(abstract_class != NULL);
jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z");
assert(miranda_method != NULL);
@@ -188,7 +188,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_JniTest_testGetMirandaMethodNative(JNI
}
// https://code.google.com/p/android/issues/detail?id=63055
-extern "C" void JNICALL Java_JniTest_testZeroLengthByteBuffers(JNIEnv* env, jclass) {
+extern "C" void JNICALL Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass) {
std::vector<uint8_t> buffer(1);
jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0);
assert(byte_buffer != NULL);
@@ -201,7 +201,7 @@ extern "C" void JNICALL Java_JniTest_testZeroLengthByteBuffers(JNIEnv* env, jcla
constexpr size_t kByteReturnSize = 7;
jbyte byte_returns[kByteReturnSize] = { 0, 1, 2, 127, -1, -2, -128 };
-extern "C" jbyte JNICALL Java_JniTest_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
+extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
jbyte b3, jbyte b4, jbyte b5, jbyte b6,
jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
// We use b1 to drive the output.
@@ -226,7 +226,7 @@ jshort short_returns[kShortReturnSize] = { 0, 1, 2, 127, 32767, -1, -2, -128,
static_cast<jshort>(0x8000) };
// The weird static_cast is because short int is only guaranteed down to -32767, not Java's -32768.
-extern "C" jshort JNICALL Java_JniTest_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
+extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
jshort s3, jshort s4, jshort s5, jshort s6,
jshort s7, jshort s8, jshort s9, jshort s10) {
// We use s1 to drive the output.
@@ -246,7 +246,7 @@ extern "C" jshort JNICALL Java_JniTest_shortMethod(JNIEnv* env, jclass klass, js
return short_returns[s1];
}
-extern "C" jboolean JNICALL Java_JniTest_booleanMethod(JNIEnv* env, jclass klass, jboolean b1,
+extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1,
jboolean b2, jboolean b3, jboolean b4,
jboolean b5, jboolean b6, jboolean b7,
jboolean b8, jboolean b9, jboolean b10) {
@@ -268,7 +268,7 @@ extern "C" jboolean JNICALL Java_JniTest_booleanMethod(JNIEnv* env, jclass klass
constexpr size_t kCharReturnSize = 8;
jchar char_returns[kCharReturnSize] = { 0, 1, 2, 127, 255, 256, 15000, 34000 };
-extern "C" jchar JNICALL Java_JniTest_charMethod(JNIEnv* env, jclass klacc, jchar c1, jchar c2,
+extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv* env, jclass klacc, jchar c1, jchar c2,
jchar c3, jchar c4, jchar c5, jchar c6,
jchar c7, jchar c8, jchar c9, jchar c10) {
// We use c1 to drive the output.
diff --git a/test/JniTest/JniTest.java b/test/004-JniTest/src/Main.java
index 33418a98b2..11c80f5a59 100644
--- a/test/JniTest/JniTest.java
+++ b/test/004-JniTest/src/Main.java
@@ -16,7 +16,7 @@
import java.lang.reflect.Method;
-class JniTest {
+public class Main {
public static void main(String[] args) {
System.loadLibrary("arttest");
testFindClassOnAttachedNativeThread();
diff --git a/test/004-NativeAllocations/expected.txt b/test/004-NativeAllocations/expected.txt
new file mode 100644
index 0000000000..f75da10caf
--- /dev/null
+++ b/test/004-NativeAllocations/expected.txt
@@ -0,0 +1 @@
+Test complete
diff --git a/test/004-NativeAllocations/info.txt b/test/004-NativeAllocations/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-NativeAllocations/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/NativeAllocations/NativeAllocations.java b/test/004-NativeAllocations/src/Main.java
index 9423b91c16..483c667915 100644
--- a/test/NativeAllocations/NativeAllocations.java
+++ b/test/004-NativeAllocations/src/Main.java
@@ -16,7 +16,7 @@
import java.lang.reflect.*;
-class NativeAllocations {
+public class Main {
static Object nativeLock = new Object();
static int nativeBytes = 0;
static Object runtime;
diff --git a/test/004-ReferenceMap/expected.txt b/test/004-ReferenceMap/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/004-ReferenceMap/expected.txt
diff --git a/test/004-ReferenceMap/info.txt b/test/004-ReferenceMap/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-ReferenceMap/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/ReferenceMap/ReferenceMap.java b/test/004-ReferenceMap/src/Main.java
index c746b688e3..f9a5498d9e 100644
--- a/test/ReferenceMap/ReferenceMap.java
+++ b/test/004-ReferenceMap/src/Main.java
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-public class ReferenceMap {
- public ReferenceMap() {
+public class Main {
+ public Main() {
}
Object f() {
@@ -41,7 +41,7 @@ public class ReferenceMap {
}
public static void main(String[] args) {
- ReferenceMap rm = new ReferenceMap();
+ Main rm = new Main();
rm.f();
}
}
diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index e5a1786102..79295549c9 100644
--- a/test/ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -271,7 +271,7 @@ struct ReferenceMap2Visitor : public StackVisitor {
// 0x0032 - 0x0033 reg=2 y Ljava/lang/Object;
// 0x0000 - 0x0033 reg=8 this LReferenceMap;
-extern "C" JNIEXPORT jint JNICALL Java_ReferenceMap_refmap(JNIEnv*, jobject, jint count) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_refmap(JNIEnv*, jobject, jint count) {
// Visitor
ScopedObjectAccess soa(Thread::Current());
ReferenceMap2Visitor mapper(soa.Self());
diff --git a/test/004-SignalTest/expected.txt b/test/004-SignalTest/expected.txt
new file mode 100644
index 0000000000..fd5ec00067
--- /dev/null
+++ b/test/004-SignalTest/expected.txt
@@ -0,0 +1,5 @@
+init signal test
+Caught NullPointerException
+Caught StackOverflowError
+signal caught
+Signal test OK
diff --git a/test/004-SignalTest/info.txt b/test/004-SignalTest/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-SignalTest/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index dfe319712e..a2dd66459c 100644
--- a/test/SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -41,7 +41,7 @@ static void signalhandler(int sig, siginfo_t* info, void* context) {
static struct sigaction oldaction;
-extern "C" JNIEXPORT void JNICALL Java_SignalTest_initSignalTest(JNIEnv*, jclass) {
+extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) {
struct sigaction action;
action.sa_sigaction = signalhandler;
sigemptyset(&action.sa_mask);
@@ -53,7 +53,7 @@ extern "C" JNIEXPORT void JNICALL Java_SignalTest_initSignalTest(JNIEnv*, jclass
sigaction(SIGSEGV, &action, &oldaction);
}
-extern "C" JNIEXPORT void JNICALL Java_SignalTest_terminateSignalTest(JNIEnv*, jclass) {
+extern "C" JNIEXPORT void JNICALL Java_Main_terminateSignalTest(JNIEnv*, jclass) {
sigaction(SIGSEGV, &oldaction, nullptr);
}
@@ -61,7 +61,7 @@ extern "C" JNIEXPORT void JNICALL Java_SignalTest_terminateSignalTest(JNIEnv*, j
// to nullptr.
char *p = nullptr;
-extern "C" JNIEXPORT jint JNICALL Java_SignalTest_testSignal(JNIEnv*, jclass) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_testSignal(JNIEnv*, jclass) {
#ifdef __arm__
// On ARM we cause a real SEGV.
*p = 'a';
diff --git a/test/SignalTest/SignalTest.java b/test/004-SignalTest/src/Main.java
index 7f15aeae76..0391592543 100644
--- a/test/SignalTest/SignalTest.java
+++ b/test/004-SignalTest/src/Main.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-class SignalTest {
+public class Main {
private static native void initSignalTest();
private static native void terminateSignalTest();
private static native int testSignal();
diff --git a/test/004-StackWalk/expected.txt b/test/004-StackWalk/expected.txt
new file mode 100644
index 0000000000..bde00246a3
--- /dev/null
+++ b/test/004-StackWalk/expected.txt
@@ -0,0 +1,4 @@
+1st call
+172001234567891011121314151617181920652310201919
+2nd call
+172001234567891011121314151617181920652310201919
diff --git a/test/004-StackWalk/info.txt b/test/004-StackWalk/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-StackWalk/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/StackWalk/StackWalk.java b/test/004-StackWalk/src/Main.java
index f7c78ff1f1..1e2a91b5a1 100644
--- a/test/StackWalk/StackWalk.java
+++ b/test/004-StackWalk/src/Main.java
@@ -1,5 +1,5 @@
-public class StackWalk {
- public StackWalk() {
+public class Main {
+ public Main() {
}
int f() {
@@ -76,18 +76,18 @@ public class StackWalk {
s4 = s18 = s19;
s += s4;
s += s18;
- refmap(0);
+ stackmap(0);
return s;
}
- native int refmap(int x);
+ native int stackmap(int x);
static {
System.loadLibrary("arttest");
}
public static void main(String[] args) {
- StackWalk st = new StackWalk();
+ Main st = new Main();
st.f();
}
}
diff --git a/test/StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc
index e404f6ab7d..30a0d5906a 100644
--- a/test/StackWalk/stack_walk_jni.cc
+++ b/test/004-StackWalk/stack_walk_jni.cc
@@ -95,13 +95,12 @@ struct TestReferenceMapVisitor : public StackVisitor {
CHECK_REGS(2, 4, 5, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 21, 25);
}
}
- LOG(INFO) << reinterpret_cast<const void*>(reg_bitmap);
return true;
}
};
-extern "C" JNIEXPORT jint JNICALL Java_StackWalk_refmap(JNIEnv*, jobject, jint count) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_stackmap(JNIEnv*, jobject, jint count) {
ScopedObjectAccess soa(Thread::Current());
CHECK_EQ(count, 0);
gJava_StackWalk_refmap_calls++;
@@ -113,7 +112,7 @@ extern "C" JNIEXPORT jint JNICALL Java_StackWalk_refmap(JNIEnv*, jobject, jint c
return count + 1;
}
-extern "C" JNIEXPORT jint JNICALL Java_StackWalk2_refmap2(JNIEnv*, jobject, jint count) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_refmap2(JNIEnv*, jobject, jint count) {
ScopedObjectAccess soa(Thread::Current());
gJava_StackWalk_refmap_calls++;
diff --git a/test/004-ThreadStress/check b/test/004-ThreadStress/check
new file mode 100755
index 0000000000..ffbb8cf17e
--- /dev/null
+++ b/test/004-ThreadStress/check
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Only compare the last line.
+tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null \ No newline at end of file
diff --git a/test/004-ThreadStress/expected.txt b/test/004-ThreadStress/expected.txt
new file mode 100644
index 0000000000..a26fb4f191
--- /dev/null
+++ b/test/004-ThreadStress/expected.txt
@@ -0,0 +1 @@
+Finishing worker
diff --git a/test/004-ThreadStress/info.txt b/test/004-ThreadStress/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-ThreadStress/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/ThreadStress/ThreadStress.java b/test/004-ThreadStress/src/Main.java
index 5dccc689c2..0c1c97d4e3 100644
--- a/test/ThreadStress/ThreadStress.java
+++ b/test/004-ThreadStress/src/Main.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
+import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -26,7 +24,7 @@ import java.util.Map;
// Run on host with:
// javac ThreadTest.java && java ThreadStress && rm *.class
-class ThreadStress implements Runnable {
+public class Main implements Runnable {
public static final boolean DEBUG = false;
@@ -80,7 +78,7 @@ class ThreadStress implements Runnable {
// Fill in the Operation[] array for each thread by laying
// down references to operation according to their desired
// frequency.
- final ThreadStress[] threadStresses = new ThreadStress[numberOfThreads];
+ final Main[] threadStresses = new Main[numberOfThreads];
for (int t = 0; t < threadStresses.length; t++) {
Operation[] operations = new Operation[operationsPerThread];
int o = 0;
@@ -98,7 +96,7 @@ class ThreadStress implements Runnable {
}
// Randomize the oepration order
Collections.shuffle(Arrays.asList(operations));
- threadStresses[t] = new ThreadStress(lock, t, operations);
+ threadStresses[t] = new Main(lock, t, operations);
}
// Enable to dump operation counds per thread to make sure its
@@ -129,9 +127,9 @@ class ThreadStress implements Runnable {
// operationsPerThread.
Thread[] runners = new Thread[numberOfThreads];
for (int r = 0; r < runners.length; r++) {
- final ThreadStress ts = threadStresses[r];
+ final Main ts = threadStresses[r];
runners[r] = new Thread("Runner thread " + r) {
- final ThreadStress threadStress = ts;
+ final Main threadStress = ts;
public void run() {
int id = threadStress.id;
System.out.println("Starting worker for " + id);
@@ -146,7 +144,7 @@ class ThreadStress implements Runnable {
+ (operationsPerThread - threadStress.nextOperation)
+ " operations remaining.");
}
- System.out.println("Finishing worker for " + id);
+ System.out.println("Finishing worker");
}
};
}
@@ -179,7 +177,7 @@ class ThreadStress implements Runnable {
private int nextOperation;
- private ThreadStress(Object lock, int id, Operation[] operations) {
+ private Main(Object lock, int id, Operation[] operations) {
this.lock = lock;
this.id = id;
this.operations = operations;
@@ -204,8 +202,8 @@ class ThreadStress implements Runnable {
}
case SIGQUIT: {
try {
- Os.kill(Os.getpid(), OsConstants.SIGQUIT);
- } catch (ErrnoException ex) {
+ SIGQUIT();
+ } catch (Exception ex) {
}
}
case SLEEP: {
@@ -267,4 +265,17 @@ class ThreadStress implements Runnable {
}
}
}
+
+ private static void SIGQUIT() throws Exception {
+ Class<?> osClass = Class.forName("android.system.Os");
+ Method getpid = osClass.getDeclaredMethod("getpid");
+ int pid = (Integer)getpid.invoke(null);
+
+ Class<?> osConstants = Class.forName("android.system.OsConstants");
+ Field sigquitField = osConstants.getDeclaredField("SIGQUIT");
+ int sigquit = (Integer)sigquitField.get(null);
+
+ Method kill = osClass.getDeclaredMethod("kill", int.class, int.class);
+ kill.invoke(null, pid, sigquit);
+ }
}
diff --git a/test/004-UnsafeTest/expected.txt b/test/004-UnsafeTest/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/004-UnsafeTest/expected.txt
diff --git a/test/004-UnsafeTest/info.txt b/test/004-UnsafeTest/info.txt
new file mode 100644
index 0000000000..00b0d9ab15
--- /dev/null
+++ b/test/004-UnsafeTest/info.txt
@@ -0,0 +1 @@
+Imported from oat tests.
diff --git a/test/UnsafeTest/UnsafeTest.java b/test/004-UnsafeTest/src/Main.java
index 9e2ff876b7..8c8ad163c5 100644
--- a/test/UnsafeTest/UnsafeTest.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -17,21 +17,21 @@
import java.lang.reflect.Field;
import sun.misc.Unsafe;
-public class UnsafeTest {
+public class Main {
static {
System.loadLibrary("arttest");
}
private static void check(int actual, int expected, String msg) {
if (actual != expected) {
- System.logE(msg + " : " + actual + " != " + expected);
+ System.out.println(msg + " : " + actual + " != " + expected);
System.exit(-1);
}
}
private static void check(long actual, long expected, String msg) {
if (actual != expected) {
- System.logE(msg + " : " + actual + " != " + expected);
+ System.out.println(msg + " : " + actual + " != " + expected);
System.exit(-1);
}
}
diff --git a/test/UnsafeTest/unsafe_test.cc b/test/004-UnsafeTest/unsafe_test.cc
index e36ee149f0..ca0e39e122 100644
--- a/test/UnsafeTest/unsafe_test.cc
+++ b/test/004-UnsafeTest/unsafe_test.cc
@@ -24,14 +24,14 @@
namespace art {
-extern "C" JNIEXPORT jint JNICALL Java_UnsafeTest_vmArrayBaseOffset(JNIEnv* env, jclass, jobject classObj) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_vmArrayBaseOffset(JNIEnv* env, jclass, jobject classObj) {
ScopedObjectAccess soa(env);
mirror::Class* klass = soa.Decode<mirror::Class*>(classObj);
return mirror::Array::DataOffset(
Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType())).Int32Value();
}
-extern "C" JNIEXPORT jint JNICALL Java_UnsafeTest_vmArrayIndexScale(JNIEnv* env, jclass, jobject classObj) {
+extern "C" JNIEXPORT jint JNICALL Java_Main_vmArrayIndexScale(JNIEnv* env, jclass, jobject classObj) {
ScopedObjectAccess soa(env);
mirror::Class* klass = soa.Decode<mirror::Class*>(classObj);
return Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType());
diff --git a/test/004-annotations/build b/test/005-annotations/build
index 1690213130..1690213130 100644
--- a/test/004-annotations/build
+++ b/test/005-annotations/build
diff --git a/test/004-annotations/expected.txt b/test/005-annotations/expected.txt
index 36b38687f9..36b38687f9 100644
--- a/test/004-annotations/expected.txt
+++ b/test/005-annotations/expected.txt
diff --git a/test/004-annotations/info.txt b/test/005-annotations/info.txt
index c8c9280c32..c8c9280c32 100644
--- a/test/004-annotations/info.txt
+++ b/test/005-annotations/info.txt
diff --git a/test/004-annotations/src/Main.java b/test/005-annotations/src/Main.java
index e44722fc42..e44722fc42 100644
--- a/test/004-annotations/src/Main.java
+++ b/test/005-annotations/src/Main.java
diff --git a/test/004-annotations/src/android/test/AnnoSimplePackage1.java b/test/005-annotations/src/android/test/AnnoSimplePackage1.java
index 629cb7dc05..629cb7dc05 100644
--- a/test/004-annotations/src/android/test/AnnoSimplePackage1.java
+++ b/test/005-annotations/src/android/test/AnnoSimplePackage1.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoArrayField.java b/test/005-annotations/src/android/test/anno/AnnoArrayField.java
index 681045c36c..681045c36c 100644
--- a/test/004-annotations/src/android/test/anno/AnnoArrayField.java
+++ b/test/005-annotations/src/android/test/anno/AnnoArrayField.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoFancyConstructor.java b/test/005-annotations/src/android/test/anno/AnnoFancyConstructor.java
index 19dadecc95..19dadecc95 100644
--- a/test/004-annotations/src/android/test/anno/AnnoFancyConstructor.java
+++ b/test/005-annotations/src/android/test/anno/AnnoFancyConstructor.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoFancyField.java b/test/005-annotations/src/android/test/anno/AnnoFancyField.java
index 855ba56146..855ba56146 100644
--- a/test/004-annotations/src/android/test/anno/AnnoFancyField.java
+++ b/test/005-annotations/src/android/test/anno/AnnoFancyField.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoFancyMethod.java b/test/005-annotations/src/android/test/anno/AnnoFancyMethod.java
index 3088866647..3088866647 100644
--- a/test/004-annotations/src/android/test/anno/AnnoFancyMethod.java
+++ b/test/005-annotations/src/android/test/anno/AnnoFancyMethod.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoFancyParameter.java b/test/005-annotations/src/android/test/anno/AnnoFancyParameter.java
index bc2ba7c293..bc2ba7c293 100644
--- a/test/004-annotations/src/android/test/anno/AnnoFancyParameter.java
+++ b/test/005-annotations/src/android/test/anno/AnnoFancyParameter.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoFancyType.java b/test/005-annotations/src/android/test/anno/AnnoFancyType.java
index 745f8386ad..745f8386ad 100644
--- a/test/004-annotations/src/android/test/anno/AnnoFancyType.java
+++ b/test/005-annotations/src/android/test/anno/AnnoFancyType.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java b/test/005-annotations/src/android/test/anno/AnnoSimpleConstructor.java
index d66b9aef29..d66b9aef29 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleConstructor.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleConstructor.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleField.java b/test/005-annotations/src/android/test/anno/AnnoSimpleField.java
index 04193d227c..04193d227c 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleField.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleField.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java b/test/005-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
index a839fa22dd..a839fa22dd 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleLocalVariable.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleMethod.java b/test/005-annotations/src/android/test/anno/AnnoSimpleMethod.java
index fcd9c0f5f2..fcd9c0f5f2 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleMethod.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleMethod.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimplePackage.java b/test/005-annotations/src/android/test/anno/AnnoSimplePackage.java
index 4aadfe7bfb..4aadfe7bfb 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimplePackage.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimplePackage.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleParameter.java b/test/005-annotations/src/android/test/anno/AnnoSimpleParameter.java
index 6e26ca3a49..6e26ca3a49 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleParameter.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleParameter.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleType.java b/test/005-annotations/src/android/test/anno/AnnoSimpleType.java
index 3bba3dbfbf..3bba3dbfbf 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleType.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleType.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleType2.java b/test/005-annotations/src/android/test/anno/AnnoSimpleType2.java
index f74b291193..f74b291193 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleType2.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleType2.java
diff --git a/test/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java b/test/005-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
index 541d82eedb..541d82eedb 100644
--- a/test/004-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
+++ b/test/005-annotations/src/android/test/anno/AnnoSimpleTypeInvis.java
diff --git a/test/004-annotations/src/android/test/anno/ExportedProperty.java b/test/005-annotations/src/android/test/anno/ExportedProperty.java
index 810d28f8fd..810d28f8fd 100644
--- a/test/004-annotations/src/android/test/anno/ExportedProperty.java
+++ b/test/005-annotations/src/android/test/anno/ExportedProperty.java
diff --git a/test/004-annotations/src/android/test/anno/FullyNoted.java b/test/005-annotations/src/android/test/anno/FullyNoted.java
index 76a7b0411d..76a7b0411d 100644
--- a/test/004-annotations/src/android/test/anno/FullyNoted.java
+++ b/test/005-annotations/src/android/test/anno/FullyNoted.java
diff --git a/test/004-annotations/src/android/test/anno/INoted.java b/test/005-annotations/src/android/test/anno/INoted.java
index b2cd007d94..b2cd007d94 100644
--- a/test/004-annotations/src/android/test/anno/INoted.java
+++ b/test/005-annotations/src/android/test/anno/INoted.java
diff --git a/test/004-annotations/src/android/test/anno/IntToString.java b/test/005-annotations/src/android/test/anno/IntToString.java
index e84fcfa3aa..e84fcfa3aa 100644
--- a/test/004-annotations/src/android/test/anno/IntToString.java
+++ b/test/005-annotations/src/android/test/anno/IntToString.java
diff --git a/test/004-annotations/src/android/test/anno/MissingAnnotation.java b/test/005-annotations/src/android/test/anno/MissingAnnotation.java
index 56da15a252..56da15a252 100644
--- a/test/004-annotations/src/android/test/anno/MissingAnnotation.java
+++ b/test/005-annotations/src/android/test/anno/MissingAnnotation.java
diff --git a/test/004-annotations/src/android/test/anno/SimplyNoted.java b/test/005-annotations/src/android/test/anno/SimplyNoted.java
index b75746589f..b75746589f 100644
--- a/test/004-annotations/src/android/test/anno/SimplyNoted.java
+++ b/test/005-annotations/src/android/test/anno/SimplyNoted.java
diff --git a/test/004-annotations/src/android/test/anno/SomeClass.java b/test/005-annotations/src/android/test/anno/SomeClass.java
index c21d68dc5f..c21d68dc5f 100644
--- a/test/004-annotations/src/android/test/anno/SomeClass.java
+++ b/test/005-annotations/src/android/test/anno/SomeClass.java
diff --git a/test/004-annotations/src/android/test/anno/SubNoted.java b/test/005-annotations/src/android/test/anno/SubNoted.java
index 2530346dcd..2530346dcd 100644
--- a/test/004-annotations/src/android/test/anno/SubNoted.java
+++ b/test/005-annotations/src/android/test/anno/SubNoted.java
diff --git a/test/004-annotations/src/android/test/anno/TestAnnotations.java b/test/005-annotations/src/android/test/anno/TestAnnotations.java
index 4eabb12b35..4eabb12b35 100644
--- a/test/004-annotations/src/android/test/anno/TestAnnotations.java
+++ b/test/005-annotations/src/android/test/anno/TestAnnotations.java
diff --git a/test/004-annotations/src/android/test/anno/package-info.java b/test/005-annotations/src/android/test/anno/package-info.java
index 74b11f99ad..74b11f99ad 100644
--- a/test/004-annotations/src/android/test/anno/package-info.java
+++ b/test/005-annotations/src/android/test/anno/package-info.java
diff --git a/test/004-annotations/src/android/test/package-info.java b/test/005-annotations/src/android/test/package-info.java
index e188cdaefd..e188cdaefd 100644
--- a/test/004-annotations/src/android/test/package-info.java
+++ b/test/005-annotations/src/android/test/package-info.java
diff --git a/test/005-args/expected.txt b/test/006-args/expected.txt
index 094fbbbcfd..094fbbbcfd 100644
--- a/test/005-args/expected.txt
+++ b/test/006-args/expected.txt
diff --git a/test/005-args/info.txt b/test/006-args/info.txt
index 08127da231..08127da231 100644
--- a/test/005-args/info.txt
+++ b/test/006-args/info.txt
diff --git a/test/005-args/src/ArgsTest.java b/test/006-args/src/ArgsTest.java
index 2b874cd29c..2b874cd29c 100644
--- a/test/005-args/src/ArgsTest.java
+++ b/test/006-args/src/ArgsTest.java
diff --git a/test/005-args/src/Main.java b/test/006-args/src/Main.java
index 8abb63c1ba..8abb63c1ba 100644
--- a/test/005-args/src/Main.java
+++ b/test/006-args/src/Main.java
diff --git a/test/006-count10/expected.txt b/test/007-count10/expected.txt
index 8b1acc12b6..8b1acc12b6 100644
--- a/test/006-count10/expected.txt
+++ b/test/007-count10/expected.txt
diff --git a/test/006-count10/info.txt b/test/007-count10/info.txt
index 08127da231..08127da231 100644
--- a/test/006-count10/info.txt
+++ b/test/007-count10/info.txt
diff --git a/test/006-count10/src/Main.java b/test/007-count10/src/Main.java
index 8fd2947dc2..8fd2947dc2 100644
--- a/test/006-count10/src/Main.java
+++ b/test/007-count10/src/Main.java
diff --git a/test/007-exceptions/expected.txt b/test/008-exceptions/expected.txt
index ef6eaff59a..ef6eaff59a 100644
--- a/test/007-exceptions/expected.txt
+++ b/test/008-exceptions/expected.txt
diff --git a/test/007-exceptions/info.txt b/test/008-exceptions/info.txt
index 08127da231..08127da231 100644
--- a/test/007-exceptions/info.txt
+++ b/test/008-exceptions/info.txt
diff --git a/test/007-exceptions/src/Main.java b/test/008-exceptions/src/Main.java
index 1f76f12460..1f76f12460 100644
--- a/test/007-exceptions/src/Main.java
+++ b/test/008-exceptions/src/Main.java
diff --git a/test/008-instanceof/src/Main.java b/test/008-instanceof/src/Main.java
deleted file mode 100644
index 77f3f515c0..0000000000
--- a/test/008-instanceof/src/Main.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Test instanceof
- */
-public class Main {
- public static void main(String args[]) {
- Iface1 face1;
- ImplA aa = new ImplA();
- ImplBSub bb = new ImplBSub();
- boolean aaOkay, bbOkay;
-
- face1 = aa;
- face1 = bb;
-
- System.out.println("iface1.mFloaty = " + face1.mFloaty + " " + face1.mWahoo);
- System.out.println("aa.mFloaty = " + aa.mFloaty + " " + aa.mWahoo);
- System.out.println("bb.mWhoami = " + bb.mWhoami);
-
- aaOkay = face1 instanceof ImplA;
- System.out.print("aaOkay (false) = ");
- System.out.println(aaOkay);
- bbOkay = face1 instanceof ImplB;
- System.out.print("bbOkay (true) = ");
- System.out.println(bbOkay);
-
- bb = (ImplBSub) face1;
- try {
- aa = (ImplA) face1;
- }
- catch (ClassCastException cce) {
- System.out.println("Caught a ClassCastException (expected)");
- }
- }
-}
diff --git a/test/008-instanceof/expected.txt b/test/009-instanceof/expected.txt
index 77fd0cbc7b..967c0bf4d5 100644
--- a/test/008-instanceof/expected.txt
+++ b/test/009-instanceof/expected.txt
@@ -4,3 +4,8 @@ bb.mWhoami = ImplB!
aaOkay (false) = false
bbOkay (true) = true
Caught a ClassCastException (expected)
+instanceof Serializable = true
+instanceof Cloneable = true
+instanceof Runnable = false
+aaOkay (false) = false
+bbOkay (true) = true
diff --git a/test/008-instanceof/info.txt b/test/009-instanceof/info.txt
index 08127da231..08127da231 100644
--- a/test/008-instanceof/info.txt
+++ b/test/009-instanceof/info.txt
diff --git a/test/008-instanceof/src/Iface1.java b/test/009-instanceof/src/Iface1.java
index d7f5376833..d7f5376833 100644
--- a/test/008-instanceof/src/Iface1.java
+++ b/test/009-instanceof/src/Iface1.java
diff --git a/test/008-instanceof/src/Iface2.java b/test/009-instanceof/src/Iface2.java
index 2b33c39ab3..2b33c39ab3 100644
--- a/test/008-instanceof/src/Iface2.java
+++ b/test/009-instanceof/src/Iface2.java
diff --git a/test/008-instanceof/src/Iface2Sub1.java b/test/009-instanceof/src/Iface2Sub1.java
index bcff8ab1fc..bcff8ab1fc 100644
--- a/test/008-instanceof/src/Iface2Sub1.java
+++ b/test/009-instanceof/src/Iface2Sub1.java
diff --git a/test/008-instanceof/src/ImplA.java b/test/009-instanceof/src/ImplA.java
index 27364f2c62..27364f2c62 100644
--- a/test/008-instanceof/src/ImplA.java
+++ b/test/009-instanceof/src/ImplA.java
diff --git a/test/008-instanceof/src/ImplB.java b/test/009-instanceof/src/ImplB.java
index 8b05702a97..8b05702a97 100644
--- a/test/008-instanceof/src/ImplB.java
+++ b/test/009-instanceof/src/ImplB.java
diff --git a/test/008-instanceof/src/ImplBSub.java b/test/009-instanceof/src/ImplBSub.java
index a94ae4d19a..a94ae4d19a 100644
--- a/test/008-instanceof/src/ImplBSub.java
+++ b/test/009-instanceof/src/ImplBSub.java
diff --git a/test/009-instanceof2/src/Main.java b/test/009-instanceof/src/Main.java
index 15a1e507fe..807ae69671 100644
--- a/test/009-instanceof2/src/Main.java
+++ b/test/009-instanceof/src/Main.java
@@ -19,10 +19,36 @@
*/
public class Main {
public static void main(String args[]) {
+ Iface1 face1;
+ ImplA aa = new ImplA();
+ ImplBSub bb = new ImplBSub();
+ boolean aaOkay, bbOkay;
+
+ face1 = aa;
+ face1 = bb;
+
+ System.out.println("iface1.mFloaty = " + face1.mFloaty + " " + face1.mWahoo);
+ System.out.println("aa.mFloaty = " + aa.mFloaty + " " + aa.mWahoo);
+ System.out.println("bb.mWhoami = " + bb.mWhoami);
+
+ aaOkay = face1 instanceof ImplA;
+ System.out.print("aaOkay (false) = ");
+ System.out.println(aaOkay);
+ bbOkay = face1 instanceof ImplB;
+ System.out.print("bbOkay (true) = ");
+ System.out.println(bbOkay);
+
+ bb = (ImplBSub) face1;
+ try {
+ aa = (ImplA) face1;
+ }
+ catch (ClassCastException cce) {
+ System.out.println("Caught a ClassCastException (expected)");
+ }
+
Iface1[] faceArray;
ImplA[] aaArray = new ImplA[5];
ImplBSub[] bbArray = new ImplBSub[5];
- boolean aaOkay, bbOkay;
faceArray = aaArray;
faceArray = bbArray;
diff --git a/test/009-instanceof2/expected.txt b/test/009-instanceof2/expected.txt
deleted file mode 100644
index 74ad202937..0000000000
--- a/test/009-instanceof2/expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-instanceof Serializable = true
-instanceof Cloneable = true
-instanceof Runnable = false
-aaOkay (false) = false
-bbOkay (true) = true
diff --git a/test/009-instanceof2/info.txt b/test/009-instanceof2/info.txt
deleted file mode 100644
index 08127da231..0000000000
--- a/test/009-instanceof2/info.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a miscellaneous test that was imported into the new-at-the-time
-runtime test framework. The test is intended to exercise basic features,
-and as such cannot be build on top of junit, since failure of such basic
-features might disrupt junit.
-
-TODO: Real description goes here.
diff --git a/test/009-instanceof2/src/Iface1.java b/test/009-instanceof2/src/Iface1.java
deleted file mode 100644
index d7f5376833..0000000000
--- a/test/009-instanceof2/src/Iface1.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Test stuff.
- */
-public interface Iface1 {
-
- public int iFunc1(int ii);
-
- public float mFloaty = 5.0f;
-
- public String mWahoo = new String("wahoo");
-}
diff --git a/test/009-instanceof2/src/Iface2Sub1.java b/test/009-instanceof2/src/Iface2Sub1.java
deleted file mode 100644
index bcff8ab1fc..0000000000
--- a/test/009-instanceof2/src/Iface2Sub1.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-/**
- * Another interface.
- */
-public interface Iface2Sub1 extends Iface2, Cloneable {
-
- //public int iFunc2(int ii);
-}
diff --git a/test/009-instanceof2/src/ImplA.java b/test/009-instanceof2/src/ImplA.java
deleted file mode 100644
index 27364f2c62..0000000000
--- a/test/009-instanceof2/src/ImplA.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-/**
- * Blah.
- */
-public class ImplA implements Iface1, Iface2 {
-
- public int iFunc1(int ii) {
- return ii+1;
- }
- public int iFunc2(int ii) {
- return ii+2;
- }
-}
diff --git a/test/009-instanceof2/src/ImplB.java b/test/009-instanceof2/src/ImplB.java
deleted file mode 100644
index 8b05702a97..0000000000
--- a/test/009-instanceof2/src/ImplB.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-/**
- * Blah.
- */
-public class ImplB implements Iface1, Iface2 {
-
- public int iFunc1(int ii) {
- return ii+10;
- }
- public int iFunc2(int ii) {
- return ii+20;
- }
-
- public static String mWhoami = new String("ImplB!");
-}
diff --git a/test/009-instanceof2/src/ImplBSub.java b/test/009-instanceof2/src/ImplBSub.java
deleted file mode 100644
index a94ae4d19a..0000000000
--- a/test/009-instanceof2/src/ImplBSub.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-/**
- * Interface test.
- */
-public class ImplBSub extends ImplB implements /*Iface2,*/ Iface2Sub1 {
-
- public int iFunc1(int ii) {
- return ii+100;
- }
- public int iFunc2(int ii) {
- return ii+200;
- }
-}
diff --git a/test/018-stack-overflow/expected.txt b/test/018-stack-overflow/expected.txt
index 98b45b7b21..cc10c0cdcf 100644
--- a/test/018-stack-overflow/expected.txt
+++ b/test/018-stack-overflow/expected.txt
@@ -1,3 +1,10 @@
-caught SOE in testSelfRecursion
+libartd run.
+caught SOE3 in testSelfRecursion
+caught SOE10 in testSelfRecursion
+caught SOE in testMutualRecursion
+SOE test done
+libart run.
+caught SOE3 in testSelfRecursion
+caught SOE10 in testSelfRecursion
caught SOE in testMutualRecursion
SOE test done
diff --git a/test/018-stack-overflow/run b/test/018-stack-overflow/run
new file mode 100755
index 0000000000..1a71a1a5d8
--- /dev/null
+++ b/test/018-stack-overflow/run
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run normal. This will be the debug build.
+echo "libartd run."
+${RUN} "${@}"
+
+# Run non-debug.
+echo "libart run."
+${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/018-stack-overflow/src/Main.java b/test/018-stack-overflow/src/Main.java
index 41adabc9ff..0961226e64 100644
--- a/test/018-stack-overflow/src/Main.java
+++ b/test/018-stack-overflow/src/Main.java
@@ -25,16 +25,38 @@ public class Main {
}
private static void testSelfRecursion() {
+// try {
+// stackOverflowTestSub0();
+// }
+// catch (StackOverflowError soe) {
+// System.out.println("caught SOE0 in testSelfRecursion");
+// }
try {
- stackOverflowTestSub(0.0, 0.0, 0.0);
+ stackOverflowTestSub3(0.0, 1.0, 2.0);
}
catch (StackOverflowError soe) {
- System.out.println("caught SOE in testSelfRecursion");
+ System.out.println("caught SOE3 in testSelfRecursion");
}
+ try {
+ stackOverflowTestSub10(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+ }
+ catch (StackOverflowError soe) {
+ System.out.println("caught SOE10 in testSelfRecursion");
+ }
+ }
+
+ private static void stackOverflowTestSub0() {
+ stackOverflowTestSub0();
+ }
+
+ private static void stackOverflowTestSub3(double pad1, double pad2, double pad3) {
+ stackOverflowTestSub3(pad1, pad2, pad3);
}
- private static void stackOverflowTestSub(double pad1, double pad2, double pad3) {
- stackOverflowTestSub(pad1, pad2, pad3);
+ private static void stackOverflowTestSub10(double pad1, double pad2, double pad3, double pad4,
+ double pad5, double pad6, double pad7, double pad8,
+ double pad9, double pad10) {
+ stackOverflowTestSub10(pad1, pad2, pad3, pad4, pad5, pad6, pad7, pad8, pad9, pad10);
}
private static void testMutualRecursion() {
diff --git a/test/050-sync-test/src/Main.java b/test/050-sync-test/src/Main.java
index ec6d732d5a..5364e2aaaa 100644
--- a/test/050-sync-test/src/Main.java
+++ b/test/050-sync-test/src/Main.java
@@ -50,13 +50,14 @@ public class Main {
one = new CpuThread(1);
two = new CpuThread(2);
- one.start();
-
- try {
- Thread.sleep(100);
- } catch (InterruptedException ie) {
- System.out.println("INTERRUPT!");
- ie.printStackTrace();
+ synchronized (one) {
+ one.start();
+ try {
+ one.wait();
+ } catch (InterruptedException ie) {
+ System.out.println("INTERRUPT!");
+ ie.printStackTrace();
+ }
}
two.start();
@@ -101,6 +102,9 @@ class CpuThread extends Thread {
//System.out.println(Thread.currentThread().getName());
synchronized (mSyncable) {
+ synchronized (this) {
+ this.notify();
+ }
for (int i = 0; i < 10; i++) {
output(mNumber);
}
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index 3ffe2f38aa..035690faa4 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -21,7 +21,7 @@ public class Main {
static void blowup(char[][] holder) {
try {
for (int i = 0; i < holder.length; ++i) {
- holder[i] = new char[128 * 1024];
+ holder[i] = new char[1024 * 1024];
}
} catch (OutOfMemoryError oome) {
ArrayMemEater.sawOome = true;
@@ -49,7 +49,7 @@ public class Main {
}
static boolean triggerArrayOOM() {
- ArrayMemEater.blowup(new char[1 * 1024 * 1024][]);
+ ArrayMemEater.blowup(new char[128 * 1024][]);
return ArrayMemEater.sawOome;
}
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 1c3c89ea3d..9ecc0a0799 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -66,25 +66,6 @@ public class Main {
test_Memory_pokeLong();
}
- /*
- * Determine if two floating point numbers are approximately equal.
- *
- * (Assumes that floating point is generally working, so we can't use
- * this for the first set of tests.)
- */
- static boolean approxEqual(float a, float b, float maxDelta) {
- if (a > b)
- return (a - b) < maxDelta;
- else
- return (b - a) < maxDelta;
- }
- static boolean approxEqual(double a, double b, double maxDelta) {
- if (a > b)
- return (a - b) < maxDelta;
- else
- return (b - a) < maxDelta;
- }
-
/**
* Will test inlining Thread.currentThread().
*/
@@ -340,39 +321,59 @@ public class Main {
}
public static void test_Math_min_F() {
- Assert.assertTrue(approxEqual(Math.min(0.0f, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.min(1.0f, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.min(0.0f, 1.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.min(0.0f, Float.MAX_VALUE), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.min(Float.MIN_VALUE, 0.0f), Float.MIN_VALUE, 0.001f));
- Assert.assertTrue(approxEqual(Math.min(Float.MIN_VALUE, Float.MAX_VALUE), Float.MIN_VALUE, 0.001f));
+ Assert.assertTrue(Float.isNaN(Math.min(1.0f, Float.NaN)));
+ Assert.assertTrue(Float.isNaN(Math.min(Float.NaN, 1.0f)));
+ Assert.assertEquals(Math.min(-0.0f, 0.0f), -0.0f);
+ Assert.assertEquals(Math.min(0.0f, -0.0f), -0.0f);
+ Assert.assertEquals(Math.min(-0.0f, -0.0f), -0.0f);
+ Assert.assertEquals(Math.min(0.0f, 0.0f), 0.0f);
+ Assert.assertEquals(Math.min(1.0f, 0.0f), 0.0f);
+ Assert.assertEquals(Math.min(0.0f, 1.0f), 0.0f);
+ Assert.assertEquals(Math.min(0.0f, Float.MAX_VALUE), 0.0f);
+ Assert.assertEquals(Math.min(Float.MIN_VALUE, 0.0f), 0.0f);
+ Assert.assertEquals(Math.min(Float.MIN_VALUE, Float.MAX_VALUE), Float.MIN_VALUE);
}
public static void test_Math_max_F() {
- Assert.assertTrue(approxEqual(Math.max(0.0f, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.max(1.0f, 0.0f), 1.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.max(0.0f, 1.0f), 1.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.max(0.0f, Float.MAX_VALUE), Float.MAX_VALUE, 0.001f));
- Assert.assertTrue(approxEqual(Math.max(Float.MIN_VALUE, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(Math.max(Float.MIN_VALUE, Float.MAX_VALUE), Float.MAX_VALUE, 0.001f));
+ Assert.assertTrue(Float.isNaN(Math.max(1.0f, Float.NaN)));
+ Assert.assertTrue(Float.isNaN(Math.max(Float.NaN, 1.0f)));
+ Assert.assertEquals(Math.max(-0.0f, 0.0f), 0.0f);
+ Assert.assertEquals(Math.max(0.0f, -0.0f), 0.0f);
+ Assert.assertEquals(Math.max(-0.0f, -0.0f), -0.0f);
+ Assert.assertEquals(Math.max(0.0f, 0.0f), 0.0f);
+ Assert.assertEquals(Math.max(1.0f, 0.0f), 1.0f);
+ Assert.assertEquals(Math.max(0.0f, 1.0f), 1.0f);
+ Assert.assertEquals(Math.max(0.0f, Float.MAX_VALUE), Float.MAX_VALUE);
+ Assert.assertEquals(Math.max(Float.MIN_VALUE, 0.0f), Float.MIN_VALUE);
+ Assert.assertEquals(Math.max(Float.MIN_VALUE, Float.MAX_VALUE), Float.MAX_VALUE);
}
public static void test_Math_min_D() {
- Assert.assertTrue(approxEqual(Math.min(0.0d, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.min(1.0d, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.min(0.0d, 1.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.min(0.0d, Double.MAX_VALUE), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.min(Double.MIN_VALUE, 0.0d), Double.MIN_VALUE, 0.001d));
- Assert.assertTrue(approxEqual(Math.min(Double.MIN_VALUE, Double.MAX_VALUE), Double.MIN_VALUE, 0.001d));
+ Assert.assertTrue(Double.isNaN(Math.min(1.0d, Double.NaN)));
+ Assert.assertTrue(Double.isNaN(Math.min(Double.NaN, 1.0d)));
+ Assert.assertEquals(Math.min(-0.0d, 0.0d), -0.0d);
+ Assert.assertEquals(Math.min(0.0d, -0.0d), -0.0d);
+ Assert.assertEquals(Math.min(-0.0d, -0.0d), -0.0d);
+ Assert.assertEquals(Math.min(0.0d, 0.0d), 0.0d);
+ Assert.assertEquals(Math.min(1.0d, 0.0d), 0.0d);
+ Assert.assertEquals(Math.min(0.0d, 1.0d), 0.0d);
+ Assert.assertEquals(Math.min(0.0d, Double.MAX_VALUE), 0.0d);
+ Assert.assertEquals(Math.min(Double.MIN_VALUE, 0.0d), 0.0d);
+ Assert.assertEquals(Math.min(Double.MIN_VALUE, Double.MAX_VALUE), Double.MIN_VALUE);
}
public static void test_Math_max_D() {
- Assert.assertTrue(approxEqual(Math.max(0.0d, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.max(1.0d, 0.0d), 1.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.max(0.0d, 1.0d), 1.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.max(0.0d, Double.MAX_VALUE), Double.MAX_VALUE, 0.001d));
- Assert.assertTrue(approxEqual(Math.max(Double.MIN_VALUE, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(Math.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE, 0.001d));
+ Assert.assertTrue(Double.isNaN(Math.max(1.0d, Double.NaN)));
+ Assert.assertTrue(Double.isNaN(Math.max(Double.NaN, 1.0d)));
+ Assert.assertEquals(Math.max(-0.0d, 0.0d), 0.0d);
+ Assert.assertEquals(Math.max(0.0d, -0.0d), 0.0d);
+ Assert.assertEquals(Math.max(-0.0d, -0.0d), -0.0d);
+ Assert.assertEquals(Math.max(0.0d, 0.0d), 0.0d);
+ Assert.assertEquals(Math.max(1.0d, 0.0d), 1.0d);
+ Assert.assertEquals(Math.max(0.0d, 1.0d), 1.0d);
+ Assert.assertEquals(Math.max(0.0d, Double.MAX_VALUE), Double.MAX_VALUE);
+ Assert.assertEquals(Math.max(Double.MIN_VALUE, 0.0d), Double.MIN_VALUE);
+ Assert.assertEquals(Math.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE);
}
public static void test_StrictMath_abs_I() {
@@ -431,39 +432,59 @@ public class Main {
}
public static void test_StrictMath_min_F() {
- Assert.assertTrue(approxEqual(StrictMath.min(0.0f, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.min(1.0f, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.min(0.0f, 1.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.min(0.0f, Float.MAX_VALUE), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.min(Float.MIN_VALUE, 0.0f), Float.MIN_VALUE, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.min(Float.MIN_VALUE, Float.MAX_VALUE), Float.MIN_VALUE, 0.001f));
+ Assert.assertTrue(Float.isNaN(StrictMath.min(1.0f, Float.NaN)));
+ Assert.assertTrue(Float.isNaN(StrictMath.min(Float.NaN, 1.0f)));
+ Assert.assertEquals(StrictMath.min(-0.0f, 0.0f), -0.0f);
+ Assert.assertEquals(StrictMath.min(0.0f, -0.0f), -0.0f);
+ Assert.assertEquals(StrictMath.min(-0.0f, -0.0f), -0.0f);
+ Assert.assertEquals(StrictMath.min(0.0f, 0.0f), 0.0f);
+ Assert.assertEquals(StrictMath.min(1.0f, 0.0f), 0.0f);
+ Assert.assertEquals(StrictMath.min(0.0f, 1.0f), 0.0f);
+ Assert.assertEquals(StrictMath.min(0.0f, Float.MAX_VALUE), 0.0f);
+ Assert.assertEquals(StrictMath.min(Float.MIN_VALUE, 0.0f), 0.0f);
+ Assert.assertEquals(StrictMath.min(Float.MIN_VALUE, Float.MAX_VALUE), Float.MIN_VALUE);
}
public static void test_StrictMath_max_F() {
- Assert.assertTrue(approxEqual(StrictMath.max(0.0f, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.max(1.0f, 0.0f), 1.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.max(0.0f, 1.0f), 1.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.max(0.0f, Float.MAX_VALUE), Float.MAX_VALUE, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.max(Float.MIN_VALUE, 0.0f), 0.0f, 0.001f));
- Assert.assertTrue(approxEqual(StrictMath.max(Float.MIN_VALUE, Float.MAX_VALUE), Float.MAX_VALUE, 0.001f));
+ Assert.assertTrue(Float.isNaN(StrictMath.max(1.0f, Float.NaN)));
+ Assert.assertTrue(Float.isNaN(StrictMath.max(Float.NaN, 1.0f)));
+ Assert.assertEquals(StrictMath.max(-0.0f, 0.0f), 0.0f);
+ Assert.assertEquals(StrictMath.max(0.0f, -0.0f), 0.0f);
+ Assert.assertEquals(StrictMath.max(-0.0f, -0.0f), -0.0f);
+ Assert.assertEquals(StrictMath.max(0.0f, 0.0f), 0.0f);
+ Assert.assertEquals(StrictMath.max(1.0f, 0.0f), 1.0f);
+ Assert.assertEquals(StrictMath.max(0.0f, 1.0f), 1.0f);
+ Assert.assertEquals(StrictMath.max(0.0f, Float.MAX_VALUE), Float.MAX_VALUE);
+ Assert.assertEquals(StrictMath.max(Float.MIN_VALUE, 0.0f), Float.MIN_VALUE);
+ Assert.assertEquals(StrictMath.max(Float.MIN_VALUE, Float.MAX_VALUE), Float.MAX_VALUE);
}
public static void test_StrictMath_min_D() {
- Assert.assertTrue(approxEqual(StrictMath.min(0.0d, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.min(1.0d, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.min(0.0d, 1.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.min(0.0d, Double.MAX_VALUE), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.min(Double.MIN_VALUE, 0.0d), Double.MIN_VALUE, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.min(Double.MIN_VALUE, Double.MAX_VALUE), Double.MIN_VALUE, 0.001d));
+ Assert.assertTrue(Double.isNaN(StrictMath.min(1.0d, Double.NaN)));
+ Assert.assertTrue(Double.isNaN(StrictMath.min(Double.NaN, 1.0d)));
+ Assert.assertEquals(StrictMath.min(-0.0d, 0.0d), -0.0d);
+ Assert.assertEquals(StrictMath.min(0.0d, -0.0d), -0.0d);
+ Assert.assertEquals(StrictMath.min(-0.0d, -0.0d), -0.0d);
+ Assert.assertEquals(StrictMath.min(0.0d, 0.0d), 0.0d);
+ Assert.assertEquals(StrictMath.min(1.0d, 0.0d), 0.0d);
+ Assert.assertEquals(StrictMath.min(0.0d, 1.0d), 0.0d);
+ Assert.assertEquals(StrictMath.min(0.0d, Double.MAX_VALUE), 0.0d);
+ Assert.assertEquals(StrictMath.min(Double.MIN_VALUE, 0.0d), 0.0d);
+ Assert.assertEquals(StrictMath.min(Double.MIN_VALUE, Double.MAX_VALUE), Double.MIN_VALUE);
}
public static void test_StrictMath_max_D() {
- Assert.assertTrue(approxEqual(StrictMath.max(0.0d, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.max(1.0d, 0.0d), 1.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.max(0.0d, 1.0d), 1.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.max(0.0d, Double.MAX_VALUE), Double.MAX_VALUE, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.max(Double.MIN_VALUE, 0.0d), 0.0d, 0.001d));
- Assert.assertTrue(approxEqual(StrictMath.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE, 0.001d));
+ Assert.assertTrue(Double.isNaN(StrictMath.max(1.0d, Double.NaN)));
+ Assert.assertTrue(Double.isNaN(StrictMath.max(Double.NaN, 1.0d)));
+ Assert.assertEquals(StrictMath.max(-0.0d, 0.0d), 0.0d);
+ Assert.assertEquals(StrictMath.max(0.0d, -0.0d), 0.0d);
+ Assert.assertEquals(StrictMath.max(-0.0d, -0.0d), -0.0d);
+ Assert.assertEquals(StrictMath.max(0.0d, 0.0d), 0.0d);
+ Assert.assertEquals(StrictMath.max(1.0d, 0.0d), 1.0d);
+ Assert.assertEquals(StrictMath.max(0.0d, 1.0d), 1.0d);
+ Assert.assertEquals(StrictMath.max(0.0d, Double.MAX_VALUE), Double.MAX_VALUE);
+ Assert.assertEquals(StrictMath.max(Double.MIN_VALUE, 0.0d), Double.MIN_VALUE);
+ Assert.assertEquals(StrictMath.max(Double.MIN_VALUE, Double.MAX_VALUE), Double.MAX_VALUE);
}
public static void test_Float_floatToRawIntBits() {
diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt
index 10406c7f8e..9f57dbd75e 100644
--- a/test/083-compiler-regressions/expected.txt
+++ b/test/083-compiler-regressions/expected.txt
@@ -14,6 +14,7 @@ b13679511Test starting
false
b13679511Test finishing
b16177324TestWrapper caught NPE as expected.
+b16230771TestWrapper caught NPE as expected.
largeFrame passes
largeFrameFloat passes
mulBy1Test passes
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 18bc674b0a..748b0dee5c 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -36,6 +36,7 @@ public class Main {
b5884080Test();
b13679511Test();
b16177324TestWrapper();
+ b16230771TestWrapper();
largeFrameTest();
largeFrameTestFloat();
mulBy1Test();
@@ -927,6 +928,28 @@ public class Main {
System.out.println("Unexpectedly retrieved all values: " + v1 + ", " + v2 + ", " + v3);
}
+ static void b16230771TestWrapper() {
+ try {
+ b16230771Test();
+ } catch (NullPointerException expected) {
+ System.out.println("b16230771TestWrapper caught NPE as expected.");
+ }
+ }
+
+ static void b16230771Test() {
+ Integer[] array = { null };
+ for (Integer i : array) {
+ try {
+ int value = i; // Null check on unboxing should fail.
+ System.out.println("Unexpectedly retrieved value " + value);
+ } catch (NullPointerException e) {
+ int value = i; // Null check on unboxing should fail.
+ // The bug was a missing null check, so this would actually cause SIGSEGV.
+ System.out.println("Unexpectedly retrieved value " + value + " in NPE catch handler");
+ }
+ }
+ }
+
static double TooManyArgs(
long l00,
long l01,
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index bed0689e82..1af4121d49 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -32,7 +32,7 @@ z (class java.lang.Character)
62 (class java.lang.Long)
14 (class java.lang.Short)
[public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)]
-[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID]
+[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR]
[void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
diff --git a/test/114-ParallelGC/expected.txt b/test/114-ParallelGC/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/114-ParallelGC/expected.txt
diff --git a/test/114-ParallelGC/info.txt b/test/114-ParallelGC/info.txt
new file mode 100644
index 0000000000..246b4e6270
--- /dev/null
+++ b/test/114-ParallelGC/info.txt
@@ -0,0 +1 @@
+Imported from oat tests. Allocates and frees objects with multiple threads.
diff --git a/test/ParallelGC/ParallelGC.java b/test/114-ParallelGC/src/Main.java
index eb9e04e70a..fb110bd035 100644
--- a/test/ParallelGC/ParallelGC.java
+++ b/test/114-ParallelGC/src/Main.java
@@ -17,11 +17,11 @@
import java.util.ArrayList;
import java.util.List;
-class ParallelGC implements Runnable {
+public class Main implements Runnable {
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[16];
for (int i = 0; i < threads.length; i++) {
- threads[i] = new Thread(new ParallelGC(i));
+ threads[i] = new Thread(new Main(i));
}
for (Thread thread : threads) {
thread.start();
@@ -33,15 +33,14 @@ class ParallelGC implements Runnable {
private final int id;
- private ParallelGC(int id) {
+ private Main(int id) {
this.id = id;
}
public void run() {
List l = new ArrayList();
- for (int i = 0; i < 1000; i++) {
+ for (int i = 0; i < 500; i++) {
l.add(new ArrayList(i));
- System.out.print(id);
}
}
}
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index a5192e1e64..2c6d1c2b60 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -75,6 +75,37 @@ public class Main {
if (m.$opt$TestReturnNewObject(m) == m) {
throw new Error("Unexpected value returned");
}
+
+ // Loop enough iterations to hope for a crash if no write barrier
+ // is emitted.
+ for (int j = 0; j < 3; j++) {
+ Main m1 = new Main();
+ $opt$SetFieldInOldObject(m1);
+ for (int i = 0; i < 1000; ++i) {
+ Object o = new byte[1024];
+ }
+ }
+
+ // Test that we do NPE checks on invokedirect.
+ Exception exception = null;
+ try {
+ invokePrivate();
+ } catch (NullPointerException e) {
+ exception = e;
+ }
+
+ if (exception == null) {
+ throw new Error("Missing NullPointerException");
+ }
+ }
+
+ public static void invokePrivate() {
+ Main m = null;
+ m.privateMethod();
+ }
+
+ private void privateMethod() {
+ Object o = new Object();
}
static int $opt$TestInvokeIntParameter(int param) {
@@ -169,4 +200,10 @@ public class Main {
public static void throwStaticMethod() {
throw new Error("Error");
}
+
+ public static void $opt$SetFieldInOldObject(Main m) {
+ m.o = new Main();
+ }
+
+ Object o;
}
diff --git a/test/700-LoadArgRegs/expected.txt b/test/700-LoadArgRegs/expected.txt
index 4977df691e..c0d5eeec9f 100644
--- a/test/700-LoadArgRegs/expected.txt
+++ b/test/700-LoadArgRegs/expected.txt
@@ -74,3 +74,4 @@ a, b, c, d, e, f, g
-91, -92, -93, -94, -95, -96, -97, -98, -99
-1, -91, -92, -93, -94, -95, -96, -97, -98, -99
1, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 2, 3, 4, 5, 6
+1234605616436508552, -8613303245920329199, 1234605616436508552
diff --git a/test/700-LoadArgRegs/src/Main.java b/test/700-LoadArgRegs/src/Main.java
index 0e6de737b3..4649d05b22 100644
--- a/test/700-LoadArgRegs/src/Main.java
+++ b/test/700-LoadArgRegs/src/Main.java
@@ -274,6 +274,14 @@ public class Main {
System.out.println(i1+", "+d1+", "+d2+", "+d3+", "+d4+", "+d5+", "+d6+", "+d7+", "+d8+", "+d9+", "+i2+", "+i3+", "+i4+", "+i5+", "+i6);
}
+ static void testRefs1(Object o1, Object o2, Object o3, Object o4, Object o5, long l1, long l2, long l3) {
+ System.out.println(l1 + ", " + l2 + ", " + l3);
+ }
+
+ static void testRefs(Object o1, Object o2, Object o3, Object o4, Object o5, long l1, long l2, long l3) {
+ testRefs1(o1, o2, o3, o4, o5, l1, l2, l3);
+ }
+
static public void main(String[] args) throws Exception {
testI();
testB();
@@ -288,5 +296,8 @@ public class Main {
testLL();
testMore(1, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 2, 3, 4, 5, 6);
+
+ Object obj = new Object();
+ testRefs(obj, obj, obj, obj, obj, 0x1122334455667788L, 0x8877665544332211L, 0x1122334455667788L);
}
}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index bf3e2aa655..f3563a4af0 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -19,11 +19,11 @@ LOCAL_PATH := $(call my-dir)
include art/build/Android.common_build.mk
LIBARTTEST_COMMON_SRC_FILES := \
- JniTest/jni_test.cc \
- SignalTest/signaltest.cc \
- ReferenceMap/stack_walk_refmap_jni.cc \
- StackWalk/stack_walk_jni.cc \
- UnsafeTest/unsafe_test.cc
+ 004-JniTest/jni_test.cc \
+ 004-SignalTest/signaltest.cc \
+ 004-ReferenceMap/stack_walk_refmap_jni.cc \
+ 004-StackWalk/stack_walk_jni.cc \
+ 004-UnsafeTest/unsafe_test.cc
ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
ifdef TARGET_2ND_ARCH
diff --git a/test/Android.oat.mk b/test/Android.oat.mk
deleted file mode 100644
index 2b142db890..0000000000
--- a/test/Android.oat.mk
+++ /dev/null
@@ -1,456 +0,0 @@
-# Copyright (C) 2011 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-LOCAL_PID := $(shell echo $$PPID)
-
-include art/build/Android.common_test.mk
-
-########################################################################
-
-# Subdirectories in art/test which contain dex files used as inputs for oat tests. Declare the
-# simplest tests (Main, HelloWorld) first, the rest are alphabetical.
-TEST_OAT_DIRECTORIES := \
- Main \
- HelloWorld \
- InterfaceTest \
- JniTest \
- SignalTest \
- NativeAllocations \
- ParallelGC \
- ReferenceMap \
- StackWalk \
- ThreadStress \
- UnsafeTest
-
-# TODO: Enable when the StackWalk2 tests are passing
-# StackWalk2 \
-
-# Create build rules for each dex file recording the dependency.
-$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval $(call build-art-test-dex,art-oat-test,$(dir), \
- $(ART_TARGET_TEST_OUT),$(LOCAL_PATH)/Android.oat.mk,ART_TEST_TARGET_OAT_$(dir)_DEX, \
- ART_TEST_HOST_OAT_$(dir)_DEX)))
-
-########################################################################
-
-include $(LOCAL_PATH)/Android.libarttest.mk
-
-ART_TEST_TARGET_OAT_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_DEFAULT_RULES :=
-ART_TEST_TARGET_OAT_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_OPTIMIZING_RULES :=
-ART_TEST_TARGET_OAT_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_INTERPRETER_RULES :=
-ART_TEST_TARGET_OAT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_RULES :=
-
-# We need dex2oat and dalvikvm on the target as well as the core image.
-TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT) $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
-ifdef TARGET_2ND_ARCH
-TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libarttest.so
-endif
-
-# Define rule to run an individual oat test on the host. Output from the test is written to the
-# host in /tmp/android-data in a directory named after test's rule name (its target) and the parent
-# process' PID (ie the PID of make). On failure the output is dumped to the console. To test for
-# success on the target device a file is created following a successful test and this is pulled
-# onto the host. If the pull fails then the file wasn't created because the test failed.
-# $(1): directory - the name of the test we're building such as HelloWorld.
-# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
-# $(3): the target (rule name), e.g. test-art-target-oat-default-HelloWorld64
-# $(4): -Xint or undefined - do we want to run with the interpreter or default.
-define define-test-art-oat-rule-target
- # Add the test dependencies to test-art-target-sync, which will be a prerequisite for the test
- # to ensure files are pushed to the device.
- TEST_ART_TARGET_SYNC_DEPS += $$(ART_TEST_TARGET_OAT_$(1)_DEX)
-
-.PHONY: $(3)
-$(3): test-art-target-sync
- $(hide) mkdir -p $(ART_HOST_TEST_DIR)/android-data-$$@
- $(hide) echo Running: $$@
- $(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID)
- $(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID)
- $(hide) $$(call ART_TEST_SKIP,$$@) && \
- adb shell "/system/bin/dalvikvm$($(2)ART_PHONY_TEST_TARGET_SUFFIX) \
- $(DALVIKVM_FLAGS) $(4) -XXlib:libartd.so -Ximage:$(ART_TARGET_TEST_DIR)/core.art \
- -classpath $(ART_TARGET_TEST_DIR)/art-oat-test-$(1).jar \
- -Djava.library.path=$(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH) $(1) \
- && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID)" \
- > $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt 2>&1 && \
- (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(2)ARCH)/$$@-$(LOCAL_PID) $(ART_HOST_TEST_DIR)/android-data-$$@ \
- && $$(call ART_TEST_PASSED,$$@)) \
- || (([ ! -f $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt ] || \
- cat $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt) && $$(call ART_TEST_FAILED,$$@))
- $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
- echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \
- rm -r $(ART_HOST_TEST_DIR)) || true
-
-endef # define-test-art-oat-rule-target
-
-# Define rules to run oat tests on the target.
-# $(1): directory - the name of the test we're building such as HelloWorld.
-# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
-define define-test-art-oat-rules-target
- # Define a phony rule to run a target oat test using the default compiler.
- default_test_rule := test-art-target-oat-default-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
- $(call define-test-art-oat-rule-target,$(1),$(2),$$(default_test_rule),)
-
- ART_TEST_TARGET_OAT_DEFAULT$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(default_test_rule)
- ART_TEST_TARGET_OAT_DEFAULT_RULES += $$(default_test_rule)
- ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES += $$(default_test_rule)
-
- optimizing_test_rule := test-art-target-oat-optimizing-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
- $(call define-test-art-oat-rule-target,$(1),$(2),$$(optimizing_test_rule), \
- -Xcompiler-option --compiler-backend=Optimizing)
-
- ART_TEST_TARGET_OAT_OPTIMIZING$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(optimizing_test_rule)
- ART_TEST_TARGET_OAT_OPTIMIZING_RULES += $$(optimizing_test_rule)
- ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES += $$(optimizing_test_rule)
-
- # Define a phony rule to run a target oat test using the interpeter.
- interpreter_test_rule := test-art-target-oat-interpreter-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
- $(call define-test-art-oat-rule-target,$(1),$(2),$$(interpreter_test_rule),-Xint)
-
- ART_TEST_TARGET_OAT_INTERPRETER$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(interpreter_test_rule)
- ART_TEST_TARGET_OAT_INTERPRETER_RULES += $$(interpreter_test_rule)
- ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES += $$(interpreter_test_rule)
-
- # Define a phony rule to run both the default and interpreter variants.
- all_test_rule := test-art-target-oat-$(1)$($(2)ART_PHONY_TEST_TARGET_SUFFIX)
-.PHONY: $$(all_test_rule)
-$$(all_test_rule): $$(default_test_rule) $$(optimizing_test_rule) $$(interpreter_test_rule)
- $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
- ART_TEST_TARGET_OAT$$($(2)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(all_test_rule)
- ART_TEST_TARGET_OAT_RULES += $$(all_test_rule)
- ART_TEST_TARGET_OAT_$(1)_RULES += $$(all_test_rule)
-
- # Clear locally defined variables.
- interpreter_test_rule :=
- default_test_rule :=
- optimizing_test_rule :=
- all_test_rule :=
-endef # define-test-art-oat-rules-target
-
-ART_TEST_HOST_OAT_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_DEFAULT_RULES :=
-ART_TEST_HOST_OAT_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_OPTIMIZING_RULES :=
-ART_TEST_HOST_OAT_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_INTERPRETER_RULES :=
-ART_TEST_HOST_OAT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_RULES :=
-
-# All tests require the host executables, libarttest and the core images.
-ART_TEST_HOST_OAT_DEPENDENCIES := \
- $(ART_HOST_EXECUTABLES) \
- $(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
- $(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
- $(HOST_CORE_IMG_OUT)
-
-ifneq ($(HOST_PREFER_32_BIT),true)
-ART_TEST_HOST_OAT_DEPENDENCIES += \
- $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
- $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
- $(2ND_HOST_CORE_IMG_OUT)
-endif
-
-# Define rule to run an individual oat test on the host. Output from the test is written to the
-# host in /tmp/android-data in a directory named after test's rule name (its target) and the parent
-# process' PID (ie the PID of make). On failure the output is dumped to the console.
-# $(1): directory - the name of the test we're building such as HelloWorld.
-# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
-# $(3): the target (rule name), e.g. test-art-host-oat-default-HelloWorld64
-# $(4): argument to dex2oat
-# $(5): argument to runtime, e.g. -Xint or undefined
-define define-test-art-oat-rule-host
- # Remove the leading / from /tmp for the test directory.
- dex_file := $$(subst /tmp,tmp,$(ART_HOST_TEST_DIR))/android-data-$(3)/oat-test-dex-$(1).jar
- oat_file := $(ART_HOST_TEST_DIR)/android-data-$(3)/dalvik-cache/$$($(2)HOST_ARCH)/$$(subst /,@,$$(dex_file))@classes.dex
-$(3): PRIVATE_DEX_FILE := /$$(dex_file)
-$(3): PRIVATE_OAT_FILE := $$(oat_file)
-.PHONY: $(3)
-$(3): $$(ART_TEST_HOST_OAT_$(1)_DEX) $(ART_TEST_HOST_OAT_DEPENDENCIES)
- $(hide) mkdir -p $(ART_HOST_TEST_DIR)/android-data-$$@/dalvik-cache/$$($(2)HOST_ARCH)
- $(hide) cp $$(realpath $$<) $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar
- $(hide) $(DEX2OATD) $(DEX2OAT_FLAGS) --runtime-arg -Xms$(DEX2OAT_XMS) --runtime-arg -Xmx$(DEX2OAT_XMX) $(4) \
- --boot-image=$$(HOST_CORE_IMG_LOCATION) \
- --dex-file=$$(PRIVATE_DEX_FILE) --oat-file=$$(PRIVATE_OAT_FILE) \
- --instruction-set=$($(2)ART_HOST_ARCH) --host --android-root=$(HOST_OUT) \
- || $$(call ART_TEST_FAILED,$$@)
- $(hide) $$(call ART_TEST_SKIP,$$@) && \
- ANDROID_DATA=$(ART_HOST_TEST_DIR)/android-data-$$@/ \
- ANDROID_ROOT=$(HOST_OUT) \
- ANDROID_LOG_TAGS='*:d' \
- LD_LIBRARY_PATH=$$($(2)ART_HOST_OUT_SHARED_LIBRARIES) \
- LD_PRELOAD=libsigchain$$(ART_HOST_SHLIB_EXTENSION) \
- $(HOST_OUT_EXECUTABLES)/dalvikvm$$($(2)ART_PHONY_TEST_HOST_SUFFIX) $(DALVIKVM_FLAGS) $(5) \
- -XXlib:libartd$(HOST_SHLIB_SUFFIX) -Ximage:$$(HOST_CORE_IMG_LOCATION) \
- -classpath $(ART_HOST_TEST_DIR)/android-data-$$@/oat-test-dex-$(1).jar \
- -Djava.library.path=$$($(2)ART_HOST_OUT_SHARED_LIBRARIES) $(1) \
- > $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt 2>&1 \
- && $$(call ART_TEST_PASSED,$$@) \
- || (([ ! -f $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt ] || \
- cat $(ART_HOST_TEST_DIR)/android-data-$$@/output.txt) && $$(call ART_TEST_FAILED,$$@))
- $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
- echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \
- rm -r $(ART_HOST_TEST_DIR)) || true
-endef # define-test-art-oat-rule-host
-
-# Define rules to run oat tests on the host.
-# $(1): directory - the name of the test we're building such as HelloWorld.
-# $(2): 2ND_ or undefined - used to differentiate between the primary and secondary architecture.
-define define-test-art-oat-rules-host
- # Create a rule to run the host oat test with the default compiler.
- default_test_rule := test-art-host-oat-default-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX)
- $(call define-test-art-oat-rule-host,$(1),$(2),$$(default_test_rule),,)
-
- ART_TEST_HOST_OAT_DEFAULT$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(default_test_rule)
- ART_TEST_HOST_OAT_DEFAULT_RULES += $$(default_test_rule)
- ART_TEST_HOST_OAT_DEFAULT_$(1)_RULES += $$(default_test_rule)
-
- # Create a rule to run the host oat test with the optimizing compiler.
- optimizing_test_rule := test-art-host-oat-optimizing-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX)
- $(call define-test-art-oat-rule-host,$(1),$(2),$$(optimizing_test_rule),--compiler-backend=Optimizing,)
-
- ART_TEST_HOST_OAT_OPTIMIZING$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(optimizing_test_rule)
- ART_TEST_HOST_OAT_OPTIMIZING_RULES += $$(optimizing_test_rule)
- ART_TEST_HOST_OAT_OPTIMIZING_$(1)_RULES += $$(optimizing_test_rule)
-
- # Create a rule to run the host oat test with the interpreter.
- interpreter_test_rule := test-art-host-oat-interpreter-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX)
- $(call define-test-art-oat-rule-host,$(1),$(2),$$(interpreter_test_rule),--compiler-filter=interpret-only,-Xint)
-
- ART_TEST_HOST_OAT_INTERPRETER$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(interpreter_test_rule)
- ART_TEST_HOST_OAT_INTERPRETER_RULES += $$(interpreter_test_rule)
- ART_TEST_HOST_OAT_INTERPRETER_$(1)_RULES += $$(interpreter_test_rule)
-
- # Define a phony rule to run both the default and interpreter variants.
- all_test_rule := test-art-host-oat-$(1)$$($(2)ART_PHONY_TEST_HOST_SUFFIX)
-.PHONY: $$(all_test_rule)
-$$(all_test_rule): $$(default_test_rule) $$(interpreter_test_rule) $$(optimizing_test_rule)
- $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
- ART_TEST_HOST_OAT$$($(2)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(all_test_rule)
- ART_TEST_HOST_OAT_RULES += $$(all_test_rule)
- ART_TEST_HOST_OAT_$(1)_RULES += $$(all_test_rule)
-
- # Clear locally defined variables.
- default_test_rule :=
- optimizing_test_rule :=
- interpreter_test_rule :=
- all_test_rule :=
-endef # define-test-art-oat-rules-host
-
-# For a given test create all the combinations of host/target, compiler and suffix such as:
-# test-art-host-oat-HelloWord or test-art-target-oat-interpreter-HelloWorld64
-# $(1): test name, e.g. HelloWorld
-# $(2): host or target
-# $(3): HOST or TARGET
-# $(4): undefined, -default, -optimizing or -interpreter
-# $(5): undefined, _DEFAULT, _OPTIMIZING or _INTERPRETER
-define define-test-art-oat-combination-for-test
- ifeq ($(2),host)
- ifneq ($(3),HOST)
- $$(error argument mismatch $(2) and ($3))
- endif
- else
- ifneq ($(2),target)
- $$(error found $(2) expected host or target)
- endif
- ifneq ($(3),TARGET)
- $$(error argument mismatch $(2) and ($3))
- endif
- endif
-
- rule_name := test-art-$(2)-oat$(4)-$(1)
- dependencies := $$(ART_TEST_$(3)_OAT$(5)_$(1)_RULES)
-
- ifeq ($$(dependencies),)
- ifneq ($(4),-optimizing)
- $$(error $$(rule_name) has no dependencies)
- endif
- endif
-
-.PHONY: $$(rule_name)
-$$(rule_name): $$(dependencies)
- $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
- # Clear locally defined variables.
- rule_name :=
- dependencies :=
-endef # define-test-art-oat-combination
-
-# Define target and host oat test rules for the differing multilib flavors and default vs
-# interpreter runs. The format of the generated rules (for running an individual test) is:
-# test-art-(host|target)-oat-(default|interpreter)-${directory}(32|64)
-# The rules are appended to various lists to enable shorter phony build rules to be built.
-# $(1): directory
-define define-test-art-oat-rules
- # Define target tests.
- ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES :=
- ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES :=
- ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES :=
- ART_TEST_TARGET_OAT_$(1)_RULES :=
- $(call define-test-art-oat-rules-target,$(1),)
- ifdef TARGET_2ND_ARCH
- $(call define-test-art-oat-rules-target,$(1),2ND_)
- endif
- $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,,))
- $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-default,_DEFAULT))
- $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-optimizing,_OPTIMIZING))
- $(call define-test-art-oat-combination-for-test,$(1),target,TARGET,-interpreter,_INTERPRETER))
-
- # Define host tests.
- ART_TEST_HOST_OAT_DEFAULT_$(1)_RULES :=
- ART_TEST_HOST_OAT_OPTIMIZING_$(1)_RULES :=
- ART_TEST_HOST_OAT_INTERPRETER_$(1)_RULES :=
- ART_TEST_HOST_OAT_$(1)_RULES :=
- $(call define-test-art-oat-rules-host,$(1),)
- ifneq ($(HOST_PREFER_32_BIT),true)
- $(call define-test-art-oat-rules-host,$(1),2ND_)
- endif
- $(call define-test-art-oat-combination-for-test,$(1),host,HOST,,)
- $(call define-test-art-oat-combination-for-test,$(1),host,HOST,-default,_DEFAULT)
- $(call define-test-art-oat-combination-for-test,$(1),host,HOST,-optimizing,_OPTIMIZING)
- $(call define-test-art-oat-combination-for-test,$(1),host,HOST,-interpreter,_INTERPRETER)
-
- # Clear locally defined variables.
- ART_TEST_TARGET_OAT_DEFAULT_$(1)_RULES :=
- ART_TEST_TARGET_OAT_OPTIMIZING_$(1)_RULES :=
- ART_TEST_TARGET_OAT_INTERPRETER_$(1)_RULES :=
- ART_TEST_TARGET_OAT_$(1)_RULES :=
- ART_TEST_HOST_OAT_DEFAULT_$(1)_RULES :=
- ART_TEST_HOST_OAT_OPTIMIZING_$(1)_RULES :=
- ART_TEST_HOST_OAT_INTERPRETER_$(1)_RULES :=
- ART_TEST_HOST_OAT_$(1)_RULES :=
-endef # define-test-art-oat-rules
-$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval $(call define-test-art-oat-rules,$(dir))))
-
-# Define all the combinations of host/target, compiler and suffix such as:
-# test-art-host-oat or test-art-target-oat-interpreter64
-# $(1): host or target
-# $(2): HOST or TARGET
-# $(3): undefined, -default, -optimizing or -interpreter
-# $(4): undefined, _DEFAULT, _OPTIMIZING or _INTERPRETER
-# $(5): undefined, 32 or 64
-define define-test-art-oat-combination
- ifeq ($(1),host)
- ifneq ($(2),HOST)
- $$(error argument mismatch $(1) and ($2))
- endif
- else
- ifneq ($(1),target)
- $$(error found $(1) expected host or target)
- endif
- ifneq ($(2),TARGET)
- $$(error argument mismatch $(1) and ($2))
- endif
- endif
-
- rule_name := test-art-$(1)-oat$(3)$(5)
- dependencies := $$(ART_TEST_$(2)_OAT$(4)$(5)_RULES)
-
- ifeq ($$(dependencies),)
- ifneq ($(3),-optimizing)
- $$(error $$(rule_name) has no dependencies)
- endif
- endif
-
-.PHONY: $$(rule_name)
-$$(rule_name): $$(dependencies)
- $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
-
- # Clear locally defined variables.
- rule_name :=
- dependencies :=
-
-endef # define-test-art-oat-combination
-
-$(eval $(call define-test-art-oat-combination,target,TARGET,,,))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-default,_DEFAULT,))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-optimizing,_OPTIMIZING,))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-interpreter,_INTERPRETER,))
-$(eval $(call define-test-art-oat-combination,target,TARGET,,,$(ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-default,_DEFAULT,$(ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-optimizing,_OPTIMIZING,$(ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-interpreter,_INTERPRETER,$(ART_PHONY_TEST_TARGET_SUFFIX)))
-ifdef TARGET_2ND_ARCH
-$(eval $(call define-test-art-oat-combination,target,TARGET,,,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-default,_DEFAULT,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-optimizing,_OPTIMIZING,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,target,TARGET,-interpreter,_INTERPRETER,$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)))
-endif
-
-$(eval $(call define-test-art-oat-combination,host,HOST,,,))
-$(eval $(call define-test-art-oat-combination,host,HOST,-default,_DEFAULT,))
-$(eval $(call define-test-art-oat-combination,host,HOST,-optimizing,_OPTIMIZING,))
-$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,))
-$(eval $(call define-test-art-oat-combination,host,HOST,,,$(ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,host,HOST,-default,_DEFAULT,$(ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,host,HOST,-optimizing,_OPTIMIZING,$(ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,$(ART_PHONY_TEST_HOST_SUFFIX)))
-ifneq ($(HOST_PREFER_32_BIT),true)
-$(eval $(call define-test-art-oat-combination,host,HOST,,,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,host,HOST,-default,_DEFAULT,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,host,HOST,-optimizing,_OPTIMIZING,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
-$(eval $(call define-test-art-oat-combination,host,HOST,-interpreter,_INTERPRETER,$(2ND_ART_PHONY_TEST_HOST_SUFFIX)))
-endif
-
-# Clear locally defined variables.
-define-test-art-oat-rule-target :=
-define-test-art-oat-rules-target :=
-define-test-art-oat-rule-host :=
-define-test-art-oat-rules-host :=
-define-test-art-oat-combination-for-test :=
-define-test-art-oat-combination :=
-ART_TEST_TARGET_OAT_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_DEFAULT_RULES :=
-ART_TEST_TARGET_OAT_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_OPTIMIZING_RULES :=
-ART_TEST_TARGET_OAT_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_INTERPRETER_RULES :=
-ART_TEST_TARGET_OAT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
-ART_TEST_TARGET_OAT_RULES :=
-ART_TEST_HOST_OAT_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_DEFAULT_RULES :=
-ART_TEST_HOST_OAT_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_OPTIMIZING_RULES :=
-ART_TEST_HOST_OAT_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_INTERPRETER_RULES :=
-ART_TEST_HOST_OAT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
-ART_TEST_HOST_OAT_RULES :=
-ART_TEST_HOST_OAT_DEPENDENCIES :=
-$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval ART_TEST_TARGET_OAT_$(dir)_DEX :=))
-$(foreach dir,$(TEST_OAT_DIRECTORIES), $(eval ART_TEST_HOST_OAT_$(dir)_DEX :=))
-TEST_OAT_DIRECTORIES :=
-LOCAL_PID :=
-LOCAL_PATH :=
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 78312d1245..ac47da6871 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -21,22 +21,23 @@ include art/build/Android.common_test.mk
TEST_ART_RUN_TESTS := $(wildcard $(LOCAL_PATH)/[0-9]*)
TEST_ART_RUN_TESTS := $(subst $(LOCAL_PATH)/,, $(TEST_ART_RUN_TESTS))
-# List all the test names for host and target excluding the -trace suffix
+# List all the test names for host and target and compiler variants.
# $(1): test name, e.g. 003-omnibus-opcodes
-# $(2): undefined or -trace
+# $(2): undefined, -trace, -gcverify or -gcstress
+# $(3): -relocate, -norelocate, -no-prebuild, or undefined.
define all-run-test-names
- test-art-host-run-test$(2)-default-$(1)32 \
- test-art-host-run-test$(2)-optimizing-$(1)32 \
- test-art-host-run-test$(2)-interpreter-$(1)32 \
- test-art-host-run-test$(2)-default-$(1)64 \
- test-art-host-run-test$(2)-optimizing-$(1)64 \
- test-art-host-run-test$(2)-interpreter-$(1)64 \
- test-art-target-run-test$(2)-default-$(1)32 \
- test-art-target-run-test$(2)-optimizing-$(1)32 \
- test-art-target-run-test$(2)-interpreter-$(1)32 \
- test-art-target-run-test$(2)-default-$(1)64 \
- test-art-target-run-test$(2)-optimizing-$(1)64 \
- test-art-target-run-test$(2)-interpreter-$(1)64
+ test-art-host-run-test$(2)-default$(3)-$(1)32 \
+ test-art-host-run-test$(2)-optimizing$(3)-$(1)32 \
+ test-art-host-run-test$(2)-interpreter$(3)-$(1)32 \
+ test-art-host-run-test$(2)-default$(3)-$(1)64 \
+ test-art-host-run-test$(2)-optimizing$(3)-$(1)64 \
+ test-art-host-run-test$(2)-interpreter$(3)-$(1)64 \
+ test-art-target-run-test$(2)-default$(3)-$(1)32 \
+ test-art-target-run-test$(2)-optimizing$(3)-$(1)32 \
+ test-art-target-run-test$(2)-interpreter$(3)-$(1)32 \
+ test-art-target-run-test$(2)-default$(3)-$(1)64 \
+ test-art-target-run-test$(2)-optimizing$(3)-$(1)64 \
+ test-art-target-run-test$(2)-interpreter$(3)-$(1)64
endef # all-run-test-names
# Tests that are timing sensitive and flaky on heavily loaded systems.
@@ -46,23 +47,48 @@ TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \
# disable timing sensitive tests on "dist" builds.
ifdef dist_goal
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),))
- ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),,-no-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcverify,-no-prebuild))
+ ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
endif
# Tests that are broken in --trace mode.
TEST_ART_BROKEN_TRACE_RUN_TESTS := \
003-omnibus-opcodes \
- 004-annotations \
+ 004-InterfaceTest \
+ 004-SignalTest \
+ 004-ThreadStress \
+ 005-annotations \
+ 012-math \
018-stack-overflow \
023-many-interfaces \
+ 027-arithmetic \
031-class-attributes \
037-inherit \
044-proxy \
046-reflect \
051-thread \
055-enum-performance \
+ 062-character-encodings \
064-field-access \
+ 074-gc-thrash \
078-polymorphic-virtual \
080-oom-throw \
082-inline-execute \
@@ -74,9 +100,27 @@ TEST_ART_BROKEN_TRACE_RUN_TESTS := \
103-string-append \
107-int-math2 \
112-double-math \
+ 114-ParallelGC \
+ 700-LoadArgRegs \
701-easy-div-rem
-ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_TRACE_RUN_TESTS), $(call all-run-test-names,$(test),-trace,))
+
+# Tests that need more than 2MB of RAM or are running into other corner cases in GC stress related
+# to OOMEs.
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \
+ 114-ParallelGC
+
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-relocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-no-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-prebuild))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,-norelocate))
+ART_TEST_KNOWN_BROKEN += $(foreach test, $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(call all-run-test-names,$(test),-gcstress,))
+
# The path where build only targets will be output, e.g.
# out/target/product/generic_x86_64/obj/PACKAGING/art-run-tests_intermediates/DATA
@@ -111,9 +155,13 @@ LOCAL_PICKUP_FILES := $(art_run_tests_dir)
include $(BUILD_PHONY_PACKAGE)
# Clear temp vars.
-TEST_ART_RUN_TEST_BUILD_RULES :=
+all-run-test-names :=
art_run_tests_dir :=
define-build-art-run-test :=
+TEST_ART_RUN_TEST_BUILD_RULES :=
+TEST_ART_TIMING_SENSITIVE_RUN_TESTS :=
+TEST_ART_BROKEN_TRACE_RUN_TESTS :=
+TEST_ART_BROKEN_GCSTRESS_RUN_TESTS :=
########################################################################
@@ -121,38 +169,138 @@ ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
# We need dex2oat and dalvikvm on the target as well as the core image.
TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(2ND_TARGET_CORE_IMG_OUT)
+# Also need libarttest.
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
+ifdef TARGET_2ND_ARCH
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_TEST_OUT)/$(TARGET_2ND_ARCH)/libarttest.so
+endif
+
# All tests require the host executables and the core images.
ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
$(HOST_CORE_IMG_OUT)
ifneq ($(HOST_PREFER_32_BIT),true)
ART_TEST_HOST_RUN_TEST_DEPENDENCIES += \
+ $(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libarttest$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_ART_HOST_OUT_SHARED_LIBRARIES)/libjavacore$(ART_HOST_SHLIB_EXTENSION) \
$(2ND_HOST_CORE_IMG_OUT)
endif
@@ -163,12 +311,15 @@ endif
# $(2): host or target
# $(3): default, optimizing or interpreter
# $(4): 32 or 64
-# $(5): run tests with tracing enabled or not: trace or undefined
+# $(5): run tests with tracing or GC verification enabled or not: trace, gcverify or undefined
+# $(6): relocate, norelocate, no-prebuild or undefined.
define define-test-art-run-test
run_test_options := $(addprefix --runtime-option ,$(DALVIKVM_FLAGS))
- run_test_rule_name := test-art-$(2)-run-test-$(3)-$(1)$(4)
+ run_test_rule_name :=
uc_host_or_target :=
prereq_rule :=
+ skip_test := false
+ uc_reloc_type :=
ifeq ($(2),host)
uc_host_or_target := HOST
run_test_options += --host
@@ -181,10 +332,39 @@ define define-test-art-run-test
$$(error found $(2) expected host or target)
endif
endif
+ ifeq ($(6),relocate)
+ uc_reloc_type := RELOCATE
+ run_test_options += --relocate --no-prebuild
+ ifneq ($(ART_TEST_RUN_TEST_RELOCATE),true)
+ skip_test := true
+ endif
+ else
+ ifeq ($(6),no-prebuild)
+ uc_reloc_type := NO_PREBUILD
+ run_test_options += --no-relocate --no-prebuild
+ ifneq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true)
+ skip_test := true
+ endif
+ else
+ ifeq ($(6),norelocate)
+ uc_reloc_type := NORELOCATE
+ run_test_options += --no-relocate --prebuild
+ ifneq ($(ART_TEST_RUN_TEST_NO_RELOCATE),true)
+ skip_test := true
+ endif
+ else
+ uc_reloc_type := PREBUILD
+ run_test_options += --relocate --prebuild
+ endif
+ endif
+ endif
uc_compiler :=
ifeq ($(3),optimizing)
uc_compiler := OPTIMIZING
run_test_options += -Xcompiler-option --compiler-backend=Optimizing
+ ifneq ($$(ART_TEST_OPTIMIZING),true)
+ skip_test := true
+ endif
else
ifeq ($(3),interpreter)
uc_compiler := INTERPRETER
@@ -206,14 +386,37 @@ define define-test-art-run-test
endif
ifeq ($(5),trace)
run_test_options += --trace
- run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(1)$(4)
+ run_test_rule_name := test-art-$(2)-run-test-trace-$(3)-$(6)-$(1)$(4)
+ ifneq ($$(ART_TEST_TRACE),true)
+ skip_test := true
+ endif
else
- ifneq (,$(5))
- $$(error found $(5) expected undefined or -trace)
+ ifeq ($(5),gcverify)
+ run_test_options += --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify \
+ --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc
+ run_test_rule_name := test-art-$(2)-run-test-gcverify-$(3)-$(6)-$(1)$(4)
+ ifneq ($$(ART_TEST_GC_VERIFY),true)
+ skip_test := true
+ endif
+ else
+ ifeq ($(5),gcstress)
+ run_test_options += --runtime-option -Xgc:SS --runtime-option -Xms2m \
+ --runtime-option -Xmx2m --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify
+ run_test_rule_name := test-art-$(2)-run-test-gcstress-$(3)-$(6)-$(1)$(4)
+ ifneq ($$(ART_TEST_GC_STRESS),true)
+ skip_test := true
+ endif
+ else
+ ifneq (,$(5))
+ $$(error found $(5) expected undefined or gcverify, gcstress or trace)
+ endif
+ run_test_rule_name := test-art-$(2)-run-test-$(3)-$(6)-$(1)$(4)
+ endif
endif
endif
- run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
- $$(run_test_options)
+ ifeq ($$(skip_test),false)
+ run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \
+ $$(run_test_options)
$$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options)
.PHONY: $$(run_test_rule_name)
$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $$(prereq_rule)
@@ -224,20 +427,28 @@ $$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $$(prereq_rule)
$$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \
echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \
rm -r $(ART_HOST_TEST_DIR)) || true
+ else
+ .PHONY: $$(run_test_rule_name)
+$$(run_test_rule_name):
+ endif
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)$(4)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$(1)_RULES += $$(run_test_rule_name)
- ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_RULES += $$(run_test_rule_name)
+ ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_compiler)_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)_RULES += $$(run_test_rule_name)
+ ART_TEST_$$(uc_host_or_target)_RUN_TEST_$(1)$(4)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL_RULES += $$(run_test_rule_name)
+ ART_TEST_$$(uc_host_or_target)_RUN_TEST_$$(uc_reloc_type)_RULES += $$(run_test_rule_name)
ART_TEST_$$(uc_host_or_target)_RUN_TEST_ALL$(4)_RULES += $$(run_test_rule_name)
# Clear locally defined variables.
+ skip_test :=
run_test_options :=
run_test_rule_name :=
uc_host_or_target :=
prereq_rule :=
+ uc_reloc_type :=
uc_compiler :=
endef # define-test-art-run-test
@@ -254,7 +465,8 @@ endef # define-test-art-run-test-group-rule
# Create rules for a group of run tests.
# $(1): test name, e.g. 003-omnibus-opcodes
# $(2): host or target
-define define-test-art-run-test-group
+# $(3): relocate, norelocate or no-prebuild, or prebuild.
+define define-test-art-run-test-group-type
group_uc_host_or_target :=
ifeq ($(2),host)
group_uc_host_or_target := HOST
@@ -266,17 +478,18 @@ define define-test-art-run-test-group
endif
endif
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
- ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- ifeq ($(2),host)
- # For now just test tracing on the host with default.
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- endif
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
do_second := false
ifeq ($(2),host)
ifneq ($$(HOST_PREFER_32_BIT),true)
@@ -288,15 +501,57 @@ define define-test-art-run-test-group
endif
endif
ifeq (true,$$(do_second))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),))
- ifeq ($(2),host)
- # For now just test tracing on the host with default.
- $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace))
- endif
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),trace,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcverify,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),default,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),interpreter,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
+ $$(eval $$(call define-test-art-run-test,$(1),$(2),optimizing,$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX),gcstress,$(3)))
endif
+endef # define-test-art-run-test-group-type
+# Create rules for a group of run tests.
+# $(1): test name, e.g. 003-omnibus-opcodes
+# $(2): host or target
+define define-test-art-run-test-group
+ group_uc_host_or_target :=
+ ifeq ($(2),host)
+ group_uc_host_or_target := HOST
+ else
+ ifeq ($(2),target)
+ group_uc_host_or_target := TARGET
+ else
+ $$(error found $(2) expected host or target)
+ endif
+ endif
+ do_second := false
+ ifeq ($(2),host)
+ ifneq ($$(HOST_PREFER_32_BIT),true)
+ do_second := true
+ endif
+ else
+ ifdef TARGET_2ND_ARCH
+ do_second := true
+ endif
+ endif
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
+ ifeq ($$(do_second),true)
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
+ endif
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),prebuild))
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),norelocate))
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),relocate))
+ $$(eval $$(call define-test-art-run-test-group-type,$(1),$(2),no-prebuild))
$$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-default-$(1), \
$$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES)))
$$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-interpreter-$(1), \
@@ -305,12 +560,22 @@ define define-test-art-run-test-group
$$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES)))
$$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1), \
$$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES)))
+ $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX), \
+ $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES)))
+ ifeq ($$(do_second),true)
+ $$(eval $$(call define-test-art-run-test-group-rule,test-art-$(2)-run-test-$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX), \
+ $$(ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES)))
+ endif
# Clear locally defined variables.
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_DEFAULT_$(1)_RULES :=
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_INTERPRETER_$(1)_RULES :=
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_OPTIMIZING_$(1)_RULES :=
ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)_RULES :=
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
+ ifeq ($$(do_second),true)
+ ART_TEST_$$(group_uc_host_or_target)_RUN_TEST_$(1)$$(2ND_ART_PHONY_TEST_$$(group_uc_host_or_target)_SUFFIX)_RULES :=
+ endif
group_uc_host_or_target :=
do_second :=
endef # define-test-art-run-test-group
@@ -318,6 +583,14 @@ endef # define-test-art-run-test-group
$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),target)))
$(foreach test, $(TEST_ART_RUN_TESTS), $(eval $(call define-test-art-run-test-group,$(test),host)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test, \
$(ART_TEST_TARGET_RUN_TEST_ALL_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default, \
@@ -326,6 +599,30 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-inter
$(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing, \
$(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate, \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default$(ART_PHONY_TEST_TARGET_SUFFIX), \
@@ -334,6 +631,38 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-inter
$(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
ifdef TARGET_2ND_ARCH
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
@@ -343,8 +672,48 @@ ifdef TARGET_2ND_ARCH
$(ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
$(ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-no-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-default-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-target-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_TARGET_SUFFIX), \
+ $(ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES)))
endif
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test, \
$(ART_TEST_HOST_RUN_TEST_ALL_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default, \
@@ -353,6 +722,30 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpr
$(ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing, \
$(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate, \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default$(ART_PHONY_TEST_HOST_SUFFIX), \
@@ -361,6 +754,38 @@ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpr
$(ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
ifneq ($(HOST_PREFER_32_BIT),true)
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
@@ -370,33 +795,160 @@ ifneq ($(HOST_PREFER_32_BIT),true)
$(ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
$(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
$(ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-no-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-prebuild$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-norelocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-default-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-interpreter-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
+ $(eval $(call define-test-art-run-test-group-rule,test-art-host-run-test-optimizing-relocate$(2ND_ART_PHONY_TEST_HOST_SUFFIX), \
+ $(ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES)))
endif
+# include libarttest build rules.
+include $(LOCAL_PATH)/Android.libarttest.mk
+
define-test-art-run-test :=
define-test-art-run-test-group-rule :=
define-test-art-run-test-group :=
-all-run-test-names :=
+TEST_ART_RUN_TESTS :=
ART_TEST_TARGET_RUN_TEST_ALL_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_TARGET_RUN_TEST_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
+ART_TEST_TARGET_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE_RULES :=
+ART_TEST_HOST_RUN_TEST_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_ALL$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_DEFAULT$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_INTERPRETER$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
ART_TEST_HOST_RUN_TEST_OPTIMIZING$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_RELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NORELOCATE$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_DEFAULT_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_INTERPRETER_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_NO_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
+ART_TEST_HOST_RUN_TEST_OPTIMIZING_PREBUILD$(2ND_ART_PHONY_TEST_HOST_SUFFIX)_RULES :=
diff --git a/test/etc/default-check b/test/etc/default-check
new file mode 100755
index 0000000000..46a095c254
--- /dev/null
+++ b/test/etc/default-check
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+diff --strip-trailing-cr -q "$1" "$2" >/dev/null \ No newline at end of file
diff --git a/test/etc/host-run-test-jar b/test/etc/host-run-test-jar
index 4265f1cbe8..d72e997fd1 100755
--- a/test/etc/host-run-test-jar
+++ b/test/etc/host-run-test-jar
@@ -10,20 +10,28 @@ msg() {
}
DEBUGGER="n"
+PREBUILD="n"
GDB="n"
+ISA="x86"
INTERPRETER="n"
VERIFY="y"
+RELOCATE="y"
OPTIMIZE="y"
INVOKE_WITH=""
DEV_MODE="n"
QUIET="n"
FLAGS=""
+COMPILER_FLAGS=""
+BUILD_BOOT_OPT=""
exe="${ANDROID_HOST_OUT}/bin/dalvikvm32"
while true; do
if [ "x$1" = "x--quiet" ]; then
QUIET="y"
shift
+ elif [ "x$1" = "x--prebuild" ]; then
+ PREBUILD="y"
+ shift
elif [ "x$1" = "x--lib" ]; then
shift
if [ "x$1" = "x" ]; then
@@ -37,7 +45,9 @@ while true; do
shift
elif [ "x$1" = "x--boot" ]; then
shift
- BOOT_OPT="$1"
+ option="$1"
+ BOOT_OPT="$option"
+ BUILD_BOOT_OPT="--boot-image=${option#-Ximage:}"
shift
elif [ "x$1" = "x--debug" ]; then
DEBUGGER="y"
@@ -65,6 +75,7 @@ while true; do
INTERPRETER="y"
shift
elif [ "x$1" = "x--64" ]; then
+ ISA="x64"
exe="${ANDROID_HOST_OUT}/bin/dalvikvm64"
shift
elif [ "x$1" = "x--no-verify" ]; then
@@ -73,10 +84,17 @@ while true; do
elif [ "x$1" = "x--no-optimize" ]; then
OPTIMIZE="n"
shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ RELOCATE="n"
+ shift
+ elif [ "x$1" = "x--relocate" ]; then
+ RELOCATE="y"
+ shift
elif [ "x$1" = "x-Xcompiler-option" ]; then
shift
option="$1"
FLAGS="${FLAGS} -Xcompiler-option $option"
+ COMPILER_FLAGS="${COMPILER_FLAGS} $option"
shift
elif [ "x$1" = "x--runtime-option" ]; then
shift
@@ -129,13 +147,40 @@ fi
if [ "$INTERPRETER" = "y" ]; then
INT_OPTS="-Xint"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --compiler-filter=interpret-only"
fi
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+if [ "$RELOCATE" = "y" ]; then
+ FLAGS="${FLAGS} -Xrelocate"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --include-patch-information"
+ # Run test sets a fairly draconian ulimit that we will likely blow right over
+ # since we are relocating. Get the total size of the /system/framework directory
+ # in 512 byte blocks and set it as the ulimit. This should be more than enough
+ # room.
+ ulimit -S $(du -c -B512 ${ANDROID_ROOT}/framework | tail -1 | cut -f1) || exit 1
+else
+ FLAGS="${FLAGS} -Xnorelocate"
+ COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -Xnorelocate --no-include-patch-information"
+fi
+mkdir_cmd="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA"
+if [ "$PREBUILD" = "y" ]; then
+ prebuild_cmd="${ANDROID_HOST_OUT}/bin/dex2oatd $COMPILER_FLAGS --instruction-set=$ISA $BUILD_BOOT_OPT --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g")"
+else
+ prebuild_cmd="true"
+fi
+
+JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+cmdline="$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline "$@"
+ if [ "$PREBUILD" = "y" ]; then
+ echo "$mkdir_cmd && $prebuild_cmd && $cmdline"
+ elif [ "$RELOCATE" = "y" ]; then
+ echo "$mkdir_cmd && $cmdline"
+ else
+ echo $cmdline
+ fi
fi
cd $ANDROID_BUILD_TOP
-$INVOKE_WITH $gdb $exe $gdbargs -XXlib:$LIB $JNI_OPTS $FLAGS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main "$@"
+$mkdir_cmd && $prebuild_cmd && LD_PRELOAD=libsigchain.so $cmdline "$@"
diff --git a/test/etc/push-and-run-prebuilt-test-jar b/test/etc/push-and-run-prebuilt-test-jar
new file mode 100755
index 0000000000..2b9604b515
--- /dev/null
+++ b/test/etc/push-and-run-prebuilt-test-jar
@@ -0,0 +1,211 @@
+#!/bin/sh
+#
+# Run the code in test.jar on the device. The jar should contain a top-level
+# class named Main to run.
+
+msg() {
+ if [ "$QUIET" = "n" ]; then
+ echo "$@"
+ fi
+}
+
+ARCHITECTURES_32="(arm|x86|mips|none)"
+ARCHITECTURES_64="(arm64|x86_64|none)"
+ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
+RELOCATE="y"
+GDB="n"
+DEBUGGER="n"
+INTERPRETER="n"
+VERIFY="y"
+OPTIMIZE="y"
+ZYGOTE=""
+QUIET="n"
+DEV_MODE="n"
+INVOKE_WITH=""
+FLAGS=""
+TARGET_SUFFIX=""
+COMPILE_FLAGS=""
+
+while true; do
+ if [ "x$1" = "x--quiet" ]; then
+ QUIET="y"
+ shift
+ elif [ "x$1" = "x--lib" ]; then
+ shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --lib" 1>&2
+ exit 1
+ fi
+ LIB="$1"
+ shift
+ elif [ "x$1" = "x-Xcompiler-option" ]; then
+ shift
+ option="$1"
+ FLAGS="${FLAGS} -Xcompiler-option $option"
+ COMPILE_FLAGS="${COMPILE_FLAGS} $option"
+ shift
+ elif [ "x$1" = "x--runtime-option" ]; then
+ shift
+ option="$1"
+ FLAGS="${FLAGS} $option"
+ shift
+ elif [ "x$1" = "x--boot" ]; then
+ shift
+ BOOT_OPT="$1"
+ BUILD_BOOT_OPT="--boot-image=${1#-Ximage:}"
+ shift
+ elif [ "x$1" = "x--relocate" ]; then
+ RELOCATE="y"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ RELOCATE="n"
+ shift
+ elif [ "x$1" = "x--debug" ]; then
+ DEBUGGER="y"
+ shift
+ elif [ "x$1" = "x--gdb" ]; then
+ GDB="y"
+ DEV_MODE="y"
+ shift
+ elif [ "x$1" = "x--zygote" ]; then
+ ZYGOTE="--zygote"
+ msg "Spawning from zygote"
+ shift
+ elif [ "x$1" = "x--dev" ]; then
+ DEV_MODE="y"
+ shift
+ elif [ "x$1" = "x--interpreter" ]; then
+ INTERPRETER="y"
+ shift
+ elif [ "x$1" = "x--invoke-with" ]; then
+ shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --invoke-with" 1>&2
+ exit 1
+ fi
+ if [ "x$INVOKE_WITH" = "x" ]; then
+ INVOKE_WITH="$1"
+ else
+ INVOKE_WITH="$INVOKE_WITH $1"
+ fi
+ shift
+ elif [ "x$1" = "x--no-verify" ]; then
+ VERIFY="n"
+ shift
+ elif [ "x$1" = "x--no-optimize" ]; then
+ OPTIMIZE="n"
+ shift
+ elif [ "x$1" = "x--" ]; then
+ shift
+ break
+ elif [ "x$1" = "x--64" ]; then
+ TARGET_SUFFIX="64"
+ ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
+ shift
+ elif expr "x$1" : "x--" >/dev/null 2>&1; then
+ echo "unknown $0 option: $1" 1>&2
+ exit 1
+ else
+ break
+ fi
+done
+
+if [ "$ZYGOTE" = "" ]; then
+ if [ "$OPTIMIZE" = "y" ]; then
+ if [ "$VERIFY" = "y" ]; then
+ DEX_OPTIMIZE="-Xdexopt:verified"
+ else
+ DEX_OPTIMIZE="-Xdexopt:all"
+ fi
+ msg "Performing optimizations"
+ else
+ DEX_OPTIMIZE="-Xdexopt:none"
+ msg "Skipping optimizations"
+ fi
+
+ if [ "$VERIFY" = "y" ]; then
+ DEX_VERIFY=""
+ msg "Performing verification"
+ else
+ DEX_VERIFY="-Xverify:none"
+ msg "Skipping verification"
+ fi
+fi
+
+msg "------------------------------"
+
+ARCH=$(adb shell ls -F /data/dalvik-cache | grep -Ewo "${ARCHITECTURES_PATTERN}")
+if [ x"$ARCH" = "x" ]; then
+ echo "Unable to determine architecture"
+ exit 1
+fi
+
+if [ "$QUIET" = "n" ]; then
+ adb shell rm -r $DEX_LOCATION
+ adb shell mkdir -p $DEX_LOCATION
+ adb push $TEST_NAME.jar $DEX_LOCATION
+ adb push $TEST_NAME-ex.jar $DEX_LOCATION
+else
+ adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
+ adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
+ adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
+ adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
+fi
+
+if [ "$DEBUGGER" = "y" ]; then
+ # Use this instead for ddms and connect by running 'ddms':
+ # DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y"
+ # TODO: add a separate --ddms option?
+
+ PORT=12345
+ msg "Waiting for jdb to connect:"
+ msg " adb forward tcp:$PORT tcp:$PORT"
+ msg " jdb -attach localhost:$PORT"
+ DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
+fi
+
+if [ "$GDB" = "y" ]; then
+ gdb="gdbserver$TARGET_SUFFIX :5039"
+ gdbargs="$exe"
+fi
+
+if [ "$INTERPRETER" = "y" ]; then
+ INT_OPTS="-Xint"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=interpret-only"
+fi
+
+JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+
+if [ "$RELOCATE" = "y" ]; then
+ RELOCATE_OPT="-Xrelocate"
+ BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
+ COMPILE_FLAGS="${COMPILE_FLAGS} --include-patch-information"
+ FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
+else
+ RELOCATE_OPT="-Xnorelocate"
+ BUILD_RELOCATE_OPT="--runtime-arg -Xnorelocate"
+fi
+
+# This is due to the fact this cmdline can get longer than the longest allowed
+# adb command and there is no way to get the exit status from a adb shell
+# command.
+cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
+ mkdir -p $DEX_LOCATION/dalvik-cache/$ARCH/ && \
+ $INVOKE_WITH /system/bin/dex2oatd $COMPILE_FLAGS $BUILD_BOOT_OPT $BUILD_RELOCATE_OPT --runtime-arg -classpath --runtime-arg $DEX_LOCATION/$TEST_NAME.jar --dex-file=$DEX_LOCATION/$TEST_NAME.jar --oat-file=$DEX_LOCATION/dalvik-cache/$ARCH/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") --instruction-set=$ARCH && \
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main $@"
+cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME")
+echo "$cmdline" > $cmdfile
+
+if [ "$DEV_MODE" = "y" ]; then
+ echo $cmdline
+fi
+
+if [ "$QUIET" = "n" ]; then
+ adb push $cmdfile $DEX_LOCATION/cmdline.sh
+else
+ adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
+fi
+
+adb shell sh $DEX_LOCATION/cmdline.sh
+
+rm -f $cmdfile
diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar
index b090c339c9..776a01146d 100755
--- a/test/etc/push-and-run-test-jar
+++ b/test/etc/push-and-run-test-jar
@@ -9,6 +9,7 @@ msg() {
fi
}
+RELOCATE="y"
GDB="n"
DEBUGGER="n"
INTERPRETER="n"
@@ -61,6 +62,12 @@ while true; do
elif [ "x$1" = "x--dev" ]; then
DEV_MODE="y"
shift
+ elif [ "x$1" = "x--relocate" ]; then
+ RELOCATE="y"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ RELOCATE="n"
+ shift
elif [ "x$1" = "x--interpreter" ]; then
INTERPRETER="y"
shift
@@ -155,8 +162,15 @@ fi
JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
+if [ "$RELOCATE" = "y" ]; then
+ RELOCATE_OPT="-Xrelocate"
+ FLAGS="${FLAGS} -Xcompiler-option --include-patch-information"
+else
+ RELOCATE_OPT="-Xnorelocate"
+fi
+
cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \
- $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
+ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $RELOCATE_OPT $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main"
if [ "$DEV_MODE" = "y" ]; then
echo $cmdline "$@"
fi
diff --git a/test/run-all-tests b/test/run-all-tests
index 25d5c5f6c5..02f46f9169 100755
--- a/test/run-all-tests
+++ b/test/run-all-tests
@@ -83,6 +83,18 @@ while true; do
elif [ "x$1" = "x--trace" ]; then
run_args="${run_args} --trace"
shift
+ elif [ "x$1" = "x--relocate" ]; then
+ run_args="${run_args} --relocate"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ run_args="${run_args} --no-relocate"
+ shift
+ elif [ "x$1" = "x--no-prebuild" ]; then
+ run_args="${run_args} --no-prebuild"
+ shift;
+ elif [ "x$1" = "x--prebuild" ]; then
+ run_args="${run_args} --prebuild"
+ shift;
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
usage="yes"
@@ -101,7 +113,8 @@ if [ "$usage" = "yes" ]; then
echo " Options are all passed to run-test; refer to that for " \
"further documentation:"
echo " --debug --dev --host --interpreter --jvm --no-optimize"
- echo " --no-verify -O --update --valgrind --zygote --64"
+ echo " --no-verify -O --update --valgrind --zygote --64 --relocate"
+ echo " --prebuild"
echo " Specific Runtime Options:"
echo " --seq Run tests one-by-one, avoiding failures caused by busy CPU"
) 1>&2
diff --git a/test/run-test b/test/run-test
index 2989f2578c..0e42efe3de 100755
--- a/test/run-test
+++ b/test/run-test
@@ -56,29 +56,32 @@ info="info.txt"
build="build"
run="run"
expected="expected.txt"
+check_cmd="check"
output="output.txt"
build_output="build-output.txt"
lib="libartd.so"
run_args="--quiet"
+prebuild_mode="yes"
target_mode="yes"
dev_mode="no"
update_mode="no"
+debug_mode="no"
+relocate="yes"
runtime="art"
usage="no"
build_only="no"
suffix64=""
+trace="false"
while true; do
if [ "x$1" = "x--host" ]; then
target_mode="no"
- RUN="${progdir}/etc/host-run-test-jar"
DEX_LOCATION=$tmp_dir
shift
elif [ "x$1" = "x--jvm" ]; then
target_mode="no"
runtime="jvm"
- RUN="${progdir}/etc/reference-run-test-classes"
NEED_DEX="false"
shift
elif [ "x$1" = "x-O" ]; then
@@ -88,6 +91,18 @@ while true; do
lib="libdvm.so"
runtime="dalvik"
shift
+ elif [ "x$1" = "x--relocate" ]; then
+ relocate="yes"
+ shift
+ elif [ "x$1" = "x--no-relocate" ]; then
+ relocate="no"
+ shift
+ elif [ "x$1" = "x--prebuild" ]; then
+ prebuild_mode="yes"
+ shift;
+ elif [ "x$1" = "x--no-prebuild" ]; then
+ prebuild_mode="no"
+ shift;
elif [ "x$1" = "x--image" ]; then
shift
image="$1"
@@ -162,7 +177,7 @@ while true; do
suffix64="64"
shift
elif [ "x$1" = "x--trace" ]; then
- run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin"
+ trace="true"
shift
elif expr "x$1" : "x--" >/dev/null 2>&1; then
echo "unknown $0 option: $1" 1>&2
@@ -174,13 +189,48 @@ while true; do
done
mkdir -p $tmp_dir
+if [ "$trace" = "true" ]; then
+ run_args="${run_args} --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin --runtime-option -Xmethod-trace-file-size:2000000"
+fi
+
+# Most interesting target architecture variables are Makefile variables, not environment variables.
+# Try to map the suffix64 flag and what we find in ${ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
+function guess_arch_name() {
+ grep32bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm|x86|mips)$'`
+ grep64bit=`ls ${ANDROID_PRODUCT_OUT}/data/art-test | grep -E '^(arm64|x86_64)$'`
+ if [ "x${suffix64}" = "x64" ]; then
+ target_arch_name=${grep64bit}
+ else
+ target_arch_name=${grep32bit}
+ fi
+}
+
+if [ "$target_mode" = "no" ]; then
+ if [ "$runtime" = "jvm" ]; then
+ RUN="${progdir}/etc/reference-run-test-classes"
+ if [ "$prebuild_mode" = "yes" ]; then
+ echo "--prebuild with --jvm is unsupported";
+ exit 1;
+ fi
+ else
+ RUN="${progdir}/etc/host-run-test-jar"
+ if [ "$prebuild_mode" = "yes" ]; then
+ run_args="${run_args} --prebuild"
+ fi
+ fi
+else
+ if [ "$prebuild_mode" = "yes" ]; then
+ RUN="${progdir}/etc/push-and-run-prebuilt-test-jar"
+ fi
+fi
+
if [ ! "$runtime" = "jvm" ]; then
run_args="${run_args} --lib $lib"
fi
if [ "$runtime" = "dalvik" ]; then
if [ "$target_mode" = "no" ]; then
- framework="${OUT}/system/framework"
+ framework="${ANDROID_PRODUCT_OUT}/system/framework"
bpath="${framework}/core.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/core-junit.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
run_args="${run_args} --boot -Xbootclasspath:${bpath}"
else
@@ -196,9 +246,17 @@ elif [ "$runtime" = "art" ]; then
export ANDROID_HOST_OUT=$ANDROID_BUILD_TOP/out/host/linux-x86
fi
run_args="${run_args} --boot -Ximage:${ANDROID_HOST_OUT}/framework/core.art"
+ run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}"
else
+ guess_arch_name
+ run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}"
run_args="${run_args} --boot -Ximage:/data/art-test/core.art"
fi
+ if [ "$relocate" = "yes" ]; then
+ run_args="${run_args} --relocate"
+ else
+ run_args="${run_args} --no-relocate"
+ fi
fi
if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
@@ -241,7 +299,7 @@ if [ "$usage" = "yes" ]; then
echo " Runtime Options:"
echo " -O Run non-debug rather than debug build (off by default)."
echo " -Xcompiler-option Pass an option to the compiler."
- echo " -runtime-option Pass an option to the runtime."
+ echo " --runtime-option Pass an option to the runtime."
echo " --debug Wait for a debugger to attach."
echo " --gdb Run under gdb; incompatible with some tests."
echo " --build-only Build test files only (off by default)."
@@ -252,6 +310,13 @@ if [ "$usage" = "yes" ]; then
echo " --zygote Spawn the process from the Zygote." \
"If used, then the"
echo " other runtime options are ignored."
+ echo " --prebuild Run dex2oat on the files before starting test. (default)"
+ echo " --no-prebuild Do not run dex2oat on the files before starting"
+ echo " the test."
+ echo " --relocate Force the use of relocating in the test, making"
+ echo " the image and oat files be relocated to a random"
+ echo " address before running. (default)"
+ echo " --no-relocate Force the use of no relocating in the test"
echo " --host Use the host-mode virtual machine."
echo " --invoke-with Pass --invoke-with option to runtime."
echo " --dalvik Use Dalvik (off by default)."
@@ -296,8 +361,13 @@ if [ '!' -r "$run" ]; then
cp "${progdir}/etc/default-run" run
fi
+if [ '!' -r "$check_cmd" ]; then
+ cp "${progdir}/etc/default-check" check
+fi
+
chmod 755 "$build"
chmod 755 "$run"
+chmod 755 "$check_cmd"
export TEST_NAME=`basename ${test_dir}`
@@ -308,7 +378,7 @@ if echo "$test_dir" | grep 089; then
elif echo "$test_dir" | grep 083; then
file_size_limit=5120
fi
-if ! ulimit "$file_size_limit"; then
+if ! ulimit -S "$file_size_limit"; then
echo "ulimit file size setting failed"
fi
@@ -364,7 +434,7 @@ else
cp "$build_output" "$output"
echo "build exit status: $build_exit" >>"$output"
fi
- diff --strip-trailing-cr -q "$expected" "$output" >/dev/null
+ ./$check_cmd "$expected" "$output"
if [ "$?" = "0" ]; then
# output == expected
good="yes"