summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/Android.common_test.mk3
-rw-r--r--build/Android.gtest.mk16
-rw-r--r--build/Android.oat.mk109
-rw-r--r--compiler/Android.bp2
-rw-r--r--compiler/common_compiler_test.cc1
-rw-r--r--compiler/common_compiler_test.h4
-rw-r--r--compiler/compiler.h3
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc7
-rw-r--r--compiler/dex/dex_to_dex_compiler.h7
-rw-r--r--compiler/driver/compiler_driver-inl.h13
-rw-r--r--compiler/driver/compiler_driver.cc129
-rw-r--r--compiler/driver/compiler_driver.h14
-rw-r--r--compiler/driver/compiler_driver_test.cc7
-rw-r--r--compiler/driver/dex_compilation_unit.cc2
-rw-r--r--compiler/driver/dex_compilation_unit.h8
-rw-r--r--compiler/image_writer.cc11
-rw-r--r--compiler/image_writer.h10
-rw-r--r--compiler/oat_writer.cc8
-rw-r--r--compiler/optimizing/builder.h15
-rw-r--r--compiler/optimizing/code_generator_arm.cc253
-rw-r--r--compiler/optimizing/code_generator_arm64.cc21
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc244
-rw-r--r--compiler/optimizing/code_generator_x86.cc2
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc2
-rw-r--r--compiler/optimizing/common_arm.h12
-rw-r--r--compiler/optimizing/common_arm64.h52
-rw-r--r--compiler/optimizing/graph_visualizer.cc6
-rw-r--r--compiler/optimizing/inliner.cc45
-rw-r--r--compiler/optimizing/instruction_builder.cc47
-rw-r--r--compiler/optimizing/instruction_builder.h11
-rw-r--r--compiler/optimizing/instruction_simplifier_arm.cc155
-rw-r--r--compiler/optimizing/instruction_simplifier_arm.h34
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.cc29
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.h4
-rw-r--r--compiler/optimizing/instruction_simplifier_shared.h27
-rw-r--r--compiler/optimizing/loop_optimization.cc6
-rw-r--r--compiler/optimizing/nodes.cc11
-rw-r--r--compiler/optimizing/nodes.h33
-rw-r--r--compiler/optimizing/nodes_arm64.h99
-rw-r--r--compiler/optimizing/nodes_shared.cc (renamed from compiler/optimizing/nodes_arm64.cc)41
-rw-r--r--compiler/optimizing/nodes_shared.h75
-rw-r--r--compiler/optimizing/optimizing_compiler.cc26
-rw-r--r--compiler/optimizing/reference_type_propagation.cc44
-rw-r--r--compiler/optimizing/reference_type_propagation.h3
-rw-r--r--compiler/optimizing/reference_type_propagation_test.cc1
-rw-r--r--compiler/optimizing/scheduler_arm64.cc4
-rw-r--r--compiler/optimizing/scheduler_arm64.h3
-rw-r--r--compiler/optimizing/ssa_builder.cc6
-rw-r--r--compiler/optimizing/ssa_builder.h3
-rw-r--r--compiler/utils/x86/assembler_x86.cc231
-rw-r--r--compiler/utils/x86/assembler_x86.h35
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc101
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc206
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h29
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc100
-rw-r--r--dex2oat/dex2oat.cc2
-rw-r--r--dex2oat/dex2oat_test.cc6
-rw-r--r--dexlayout/dex_ir.cc18
-rw-r--r--dexlayout/dex_ir.h2
-rw-r--r--dexlayout/dexlayout_test.cc2
-rw-r--r--disassembler/disassembler_x86.cc2
-rw-r--r--oatdump/Android.mk4
-rw-r--r--oatdump/oatdump.cc16
-rw-r--r--patchoat/patchoat.cc4
-rw-r--r--profman/profman.cc2
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/art_field-inl.h15
-rw-r--r--runtime/art_field.cc4
-rw-r--r--runtime/art_field.h2
-rw-r--r--runtime/art_method-inl.h13
-rw-r--r--runtime/class_linker-inl.h31
-rw-r--r--runtime/class_linker.cc264
-rw-r--r--runtime/class_linker.h8
-rw-r--r--runtime/class_linker_test.cc8
-rw-r--r--runtime/common_runtime_test.h6
-rw-r--r--runtime/dex_file_annotations.cc92
-rw-r--r--runtime/dexopt_test.cc5
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h8
-rw-r--r--runtime/entrypoints/quick/quick_dexcache_entrypoints.cc34
-rw-r--r--runtime/gc/collector_type.h2
-rw-r--r--runtime/gc/gc_cause.cc1
-rw-r--r--runtime/gc/gc_cause.h2
-rw-r--r--runtime/gc/heap.cc17
-rw-r--r--runtime/gc/heap.h3
-rw-r--r--runtime/gc/space/image_space.cc4
-rw-r--r--runtime/hprof/hprof.cc24
-rw-r--r--runtime/image.cc2
-rw-r--r--runtime/interpreter/unstarted_runtime.cc145
-rw-r--r--runtime/interpreter/unstarted_runtime.h7
-rw-r--r--runtime/interpreter/unstarted_runtime_list.h2
-rw-r--r--runtime/interpreter/unstarted_runtime_test.cc289
-rw-r--r--runtime/jit/jit_code_cache.cc31
-rw-r--r--runtime/jit/jit_code_cache.h3
-rw-r--r--runtime/jit/profile_compilation_info.cc581
-rw-r--r--runtime/jit/profile_compilation_info.h270
-rw-r--r--runtime/jit/profile_compilation_info_test.cc343
-rw-r--r--runtime/jit/profile_saver.cc12
-rw-r--r--runtime/jit/profiling_info.h2
-rw-r--r--runtime/method_handles.cc44
-rw-r--r--runtime/mirror/class.cc3
-rw-r--r--runtime/mirror/dex_cache-inl.h110
-rw-r--r--runtime/mirror/dex_cache.cc20
-rw-r--r--runtime/mirror/dex_cache.h77
-rw-r--r--runtime/mirror/dex_cache_test.cc3
-rw-r--r--runtime/mirror/emulated_stack_frame.h4
-rw-r--r--runtime/native/dalvik_system_DexFile.cc102
-rw-r--r--runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc198
-rw-r--r--runtime/native/java_lang_DexCache.cc2
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/openjdkjvmti/ti_class.cc25
-rw-r--r--runtime/openjdkjvmti/ti_field.cc22
-rw-r--r--runtime/openjdkjvmti/ti_method.cc22
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc2
-rw-r--r--runtime/runtime.cc2
-rw-r--r--runtime/safe_map.h10
-rw-r--r--runtime/thread.cc27
-rw-r--r--runtime/utils/dex_cache_arrays_layout-inl.h19
-rw-r--r--runtime/verifier/method_verifier.cc18
-rw-r--r--test/021-string2/src/Main.java7
-rw-r--r--test/155-java-set-resolved-type/src/Main.java6
-rw-r--r--test/551-checker-shifter-operand/src/Main.java524
-rw-r--r--test/593-checker-shift-and-simplifier/src/Main.java13
-rw-r--r--test/624-checker-stringops/src/Main.java6
-rw-r--r--test/626-const-class-linking/clear_dex_cache_types.cc3
-rw-r--r--test/638-no-line-number/build25
-rw-r--r--test/638-no-line-number/expected.txt5
-rw-r--r--test/638-no-line-number/info.txt1
-rw-r--r--test/638-no-line-number/src/Main.java34
-rw-r--r--test/909-attach-agent/attach.cc12
-rw-r--r--test/910-methods/expected.txt2
-rw-r--r--test/912-classes/expected.txt4
-rw-r--r--test/918-fields/expected.txt4
-rw-r--r--test/918-fields/src/Main.java5
-rwxr-xr-xtest/948-change-annotations/build17
-rw-r--r--test/948-change-annotations/expected.txt21
-rw-r--r--test/948-change-annotations/info.txt1
-rwxr-xr-xtest/948-change-annotations/run17
-rw-r--r--test/948-change-annotations/src/AddAnnotationsTest.java70
-rw-r--r--test/948-change-annotations/src/ChangeAnnotationValues.java64
-rw-r--r--test/948-change-annotations/src/Main.java90
-rw-r--r--test/948-change-annotations/src/RemoveAnnotationsTest.java55
-rw-r--r--test/948-change-annotations/src/TestCase.java19
-rw-r--r--test/948-change-annotations/src/TestClassAnnotation1.java22
-rw-r--r--test/948-change-annotations/src/TestClassAnnotation2.java22
-rw-r--r--test/948-change-annotations/src/TestMethodAnnotation1.java22
-rw-r--r--test/948-change-annotations/src/TestMethodAnnotation2.java22
-rw-r--r--test/948-change-annotations/src/Transform.java (renamed from runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h)19
-rw-r--r--test/949-in-memory-transform/expected.txt2
-rw-r--r--test/949-in-memory-transform/info.txt4
-rwxr-xr-xtest/949-in-memory-transform/run17
-rw-r--r--test/949-in-memory-transform/src/Main.java124
-rw-r--r--test/956-methodhandles/src/Main.java34
-rw-r--r--test/Android.run-test.mk85
-rwxr-xr-xtest/etc/run-test-jar2
-rwxr-xr-xtest/run-test10
-rw-r--r--test/testrunner/env.py3
-rwxr-xr-xtest/testrunner/testrunner.py158
-rw-r--r--tools/libcore_failures.txt12
158 files changed, 5384 insertions, 1550 deletions
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index b7a2379dc9..1591e34885 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -66,9 +66,6 @@ ART_TEST_OPTIMIZING ?= true
# Do you want to test the optimizing compiler with graph coloring register allocation?
ART_TEST_OPTIMIZING_GRAPH_COLOR ?= $(ART_TEST_FULL)
-# Do we want to test a non-PIC-compiled core image?
-ART_TEST_NPIC_IMAGE ?= $(ART_TEST_FULL)
-
# Do we want to test PIC-compiled tests ("apps")?
ART_TEST_PIC_TEST ?= $(ART_TEST_FULL)
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c319b1a387..b661e001c8 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -123,16 +123,16 @@ ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE
ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_DEFAULT_64) $(TARGET_CORE_IMAGE_DEFAULT_32)
ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
- $(HOST_CORE_IMAGE_optimizing_pic_64) \
- $(HOST_CORE_IMAGE_optimizing_pic_32) \
- $(HOST_CORE_IMAGE_interpreter_pic_64) \
- $(HOST_CORE_IMAGE_interpreter_pic_32) \
+ $(HOST_CORE_IMAGE_optimizing_64) \
+ $(HOST_CORE_IMAGE_optimizing_32) \
+ $(HOST_CORE_IMAGE_interpreter_64) \
+ $(HOST_CORE_IMAGE_interpreter_32) \
$(HOST_OUT_EXECUTABLES)/patchoatd
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
- $(TARGET_CORE_IMAGE_optimizing_pic_64) \
- $(TARGET_CORE_IMAGE_optimizing_pic_32) \
- $(TARGET_CORE_IMAGE_interpreter_pic_64) \
- $(TARGET_CORE_IMAGE_interpreter_pic_32) \
+ $(TARGET_CORE_IMAGE_optimizing_64) \
+ $(TARGET_CORE_IMAGE_optimizing_32) \
+ $(TARGET_CORE_IMAGE_interpreter_64) \
+ $(TARGET_CORE_IMAGE_interpreter_32) \
$(TARGET_OUT_EXECUTABLES)/patchoatd
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 0086660a05..f53740e6e0 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -38,11 +38,10 @@ endif
# Use dex2oat debug version for better error reporting
# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
-# $(2): pic/no-pic
-# $(3): 2ND_ or undefined, 2ND_ for 32-bit host builds.
-# $(4): wrapper, e.g., valgrind.
-# $(5): dex2oat suffix, e.g, valgrind requires 32 right now.
-# $(6): multi-image.
+# $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds.
+# $(3): wrapper, e.g., valgrind.
+# $(4): dex2oat suffix, e.g, valgrind requires 32 right now.
+# $(5): multi-image.
# NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
# run-test --no-image
define create-core-oat-host-rules
@@ -50,7 +49,6 @@ define create-core-oat-host-rules
core_image_name :=
core_oat_name :=
core_infix :=
- core_pic_infix :=
core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
ifeq ($(1),optimizing)
@@ -70,19 +68,8 @@ define create-core-oat-host-rules
$$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing)
endif
- ifeq ($(2),pic)
- core_compile_options += --compile-pic
- endif
- ifeq ($(2),no-pic)
- core_pic_infix := -npic
- endif
- ifneq ($(filter-out pic no-pic,$(2)),)
- # Technically this test is not precise, but hopefully good enough.
- $$(error found $(2) expected pic or no-pic)
- endif
-
- # If $(6) is true, generate a multi-image.
- ifeq ($(6),true)
+ # If $(5) is true, generate a multi-image.
+ ifeq ($(5),true)
core_multi_infix := -multi
core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar
core_multi_group := _multi
@@ -92,20 +79,20 @@ define create-core-oat-host-rules
core_multi_group :=
endif
- core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_IMG_SUFFIX)
- core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_OAT_SUFFIX)
+ core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_IMG_SUFFIX)
+ core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(3)$(CORE_OAT_SUFFIX)
# Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
- ifeq ($(3),)
- $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_64 := $$(core_image_name)
+ ifeq ($(2),)
+ $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name)
else
- $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_32 := $$(core_image_name)
+ $(3)HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name)
endif
- $(4)HOST_CORE_IMG_OUTS += $$(core_image_name)
- $(4)HOST_CORE_OAT_OUTS += $$(core_oat_name)
+ $(3)HOST_CORE_IMG_OUTS += $$(core_image_name)
+ $(3)HOST_CORE_OAT_OUTS += $$(core_oat_name)
# If we have a wrapper, make the target phony.
- ifneq ($(4),)
+ ifneq ($(3),)
.PHONY: $$(core_image_name)
endif
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
@@ -115,15 +102,15 @@ $$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param)
$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
@echo "host dex2oat: $$@"
@mkdir -p $$(dir $$@)
- $$(hide) $(4) $$(DEX2OAT)$(5) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ $$(hide) $(3) $$(DEX2OAT)$(4) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
--runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
--image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(HOST_CORE_DEX_FILES)) \
$$(addprefix --dex-location=,$$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
--oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
- --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \
- $$(LOCAL_$(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
+ --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(2)ART_HOST_ARCH) \
+ $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
--host --android-root=$$(HOST_OUT) --include-patch-information \
- --generate-debug-info --generate-build-id \
+ --generate-debug-info --generate-build-id --compile-pic \
$$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS)
$$(core_oat_name): $$(core_image_name)
@@ -134,7 +121,6 @@ $$(core_oat_name): $$(core_image_name)
core_image_name :=
core_oat_name :=
core_infix :=
- core_pic_infix :=
endef # create-core-oat-host-rules
# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
@@ -142,12 +128,10 @@ endef # create-core-oat-host-rules
# $(3): dex2oat suffix.
# $(4): multi-image.
define create-core-oat-host-rule-combination
- $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3),$(4))
- $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3),$(4))
+ $(call create-core-oat-host-rules,$(1),,$(2),$(3),$(4))
ifneq ($(HOST_PREFER_32_BIT),true)
- $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3),$(4))
- $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3),$(4))
+ $(call create-core-oat-host-rules,$(1),2ND_,$(2),$(3),$(4))
endif
endef
@@ -173,7 +157,6 @@ define create-core-oat-target-rules
core_image_name :=
core_oat_name :=
core_infix :=
- core_pic_infix :=
core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY)
ifeq ($(1),optimizing)
@@ -195,35 +178,24 @@ define create-core-oat-target-rules
$$(error found $(1) expected interpreter, interpreter-access-checks, or optimizing)
endif
- ifeq ($(2),pic)
- core_compile_options += --compile-pic
- endif
- ifeq ($(2),no-pic)
- core_pic_infix := -npic
- endif
- ifneq ($(filter-out pic no-pic,$(2)),)
- #Technically this test is not precise, but hopefully good enough.
- $$(error found $(2) expected pic or no-pic)
- endif
-
- core_image_name := $($(3)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX)
- core_oat_name := $($(3)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX)
+ core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(3)$(CORE_IMG_SUFFIX)
+ core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(3)$(CORE_OAT_SUFFIX)
# Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
- ifeq ($(3),)
+ ifeq ($(2),)
ifdef TARGET_2ND_ARCH
- $(4)TARGET_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name)
+ $(3)TARGET_CORE_IMAGE_$(1)_64 := $$(core_image_name)
else
- $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+ $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
endif
else
- $(4)TARGET_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name)
+ $(3)TARGET_CORE_IMAGE_$(1)_32 := $$(core_image_name)
endif
- $(4)TARGET_CORE_IMG_OUTS += $$(core_image_name)
- $(4)TARGET_CORE_OAT_OUTS += $$(core_oat_name)
+ $(3)TARGET_CORE_IMG_OUTS += $$(core_image_name)
+ $(3)TARGET_CORE_OAT_OUTS += $$(core_oat_name)
# If we have a wrapper, make the target phony.
- ifneq ($(4),)
+ ifneq ($(3),)
.PHONY: $$(core_image_name)
endif
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
@@ -237,11 +209,11 @@ $$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
--image-classes=$$(PRELOADED_CLASSES) $$(addprefix --dex-file=,$$(TARGET_CORE_DEX_FILES)) \
$$(addprefix --dex-location=,$$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
--oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
- --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(3)TARGET_ARCH) \
- --instruction-set-variant=$$($(3)DEX2OAT_TARGET_CPU_VARIANT) \
- --instruction-set-features=$$($(3)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
+ --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(2)TARGET_ARCH) \
+ --instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
+ --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
--android-root=$$(PRODUCT_OUT)/system --include-patch-information \
- --generate-debug-info --generate-build-id \
+ --generate-debug-info --generate-build-id --compile-pic \
$$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
$$(core_oat_name): $$(core_image_name)
@@ -252,19 +224,16 @@ $$(core_oat_name): $$(core_image_name)
core_image_name :=
core_oat_name :=
core_infix :=
- core_pic_infix :=
endef # create-core-oat-target-rules
# $(1): compiler - optimizing, interpreter or interpreter-access-checks.
# $(2): wrapper.
# $(3): dex2oat suffix.
define create-core-oat-target-rule-combination
- $(call create-core-oat-target-rules,$(1),no-pic,,$(2),$(3))
- $(call create-core-oat-target-rules,$(1),pic,,$(2),$(3))
+ $(call create-core-oat-target-rules,$(1),,$(2),$(3))
ifdef TARGET_2ND_ARCH
- $(call create-core-oat-target-rules,$(1),no-pic,2ND_,$(2),$(3))
- $(call create-core-oat-target-rules,$(1),pic,2ND_,$(2),$(3))
+ $(call create-core-oat-target-rules,$(1),2ND_,$(2),$(3))
endif
endef
@@ -284,7 +253,7 @@ valgrind-test-art-host-dex2oat: valgrind-test-art-host-dex2oat-host valgrind-tes
# Define a default core image that can be used for things like gtests that
# need some image to run, but don't otherwise care which image is used.
-HOST_CORE_IMAGE_DEFAULT_32 := $(HOST_CORE_IMAGE_optimizing_pic_32)
-HOST_CORE_IMAGE_DEFAULT_64 := $(HOST_CORE_IMAGE_optimizing_pic_64)
-TARGET_CORE_IMAGE_DEFAULT_32 := $(TARGET_CORE_IMAGE_optimizing_pic_32)
-TARGET_CORE_IMAGE_DEFAULT_64 := $(TARGET_CORE_IMAGE_optimizing_pic_64)
+HOST_CORE_IMAGE_DEFAULT_32 := $(HOST_CORE_IMAGE_optimizing_32)
+HOST_CORE_IMAGE_DEFAULT_64 := $(HOST_CORE_IMAGE_optimizing_64)
+TARGET_CORE_IMAGE_DEFAULT_32 := $(TARGET_CORE_IMAGE_optimizing_32)
+TARGET_CORE_IMAGE_DEFAULT_64 := $(TARGET_CORE_IMAGE_optimizing_64)
diff --git a/compiler/Android.bp b/compiler/Android.bp
index f6a4db49fb..f5589cd7a3 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -111,6 +111,7 @@ art_cc_defaults {
"optimizing/instruction_simplifier_shared.cc",
"optimizing/intrinsics_arm.cc",
"optimizing/intrinsics_arm_vixl.cc",
+ "optimizing/nodes_shared.cc",
"utils/arm/assembler_arm.cc",
"utils/arm/assembler_arm_vixl.cc",
"utils/arm/assembler_thumb2.cc",
@@ -127,7 +128,6 @@ art_cc_defaults {
"optimizing/scheduler_arm64.cc",
"optimizing/instruction_simplifier_arm64.cc",
"optimizing/intrinsics_arm64.cc",
- "optimizing/nodes_arm64.cc",
"utils/arm64/assembler_arm64.cc",
"utils/arm64/jni_macro_assembler_arm64.cc",
"utils/arm64/managed_register_arm64.cc",
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 2f9164c0e0..d89cdbabf8 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -175,6 +175,7 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind,
InstructionSet isa,
size_t number_of_threads) {
compiler_options_->boot_image_ = true;
+ compiler_options_->SetCompilerFilter(GetCompilerFilter());
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
kind,
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 0d45a50053..98dcf20714 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -77,6 +77,10 @@ class CommonCompilerTest : public CommonRuntimeTest {
virtual ProfileCompilationInfo* GetProfileCompilationInfo();
+ virtual CompilerFilter::Filter GetCompilerFilter() const {
+ return CompilerFilter::kDefaultCompilerFilter;
+ }
+
virtual void TearDown();
void CompileClass(mirror::ClassLoader* class_loader, const char* class_name)
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 2ca0b77a73..908d3669ed 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -27,6 +27,7 @@ namespace jit {
class JitCodeCache;
}
namespace mirror {
+ class ClassLoader;
class DexCache;
}
@@ -63,7 +64,7 @@ class Compiler {
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache) const = 0;
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index d4f6545c59..76aeaa55d7 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -284,16 +284,13 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
}
uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(unit_.GetClassLoader())));
ClassLinker* class_linker = unit_.GetClassLinker();
ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
GetDexFile(),
method_idx,
unit_.GetDexCache(),
- class_loader,
+ unit_.GetClassLoader(),
/* referrer */ nullptr,
kVirtual);
@@ -330,7 +327,7 @@ CompiledMethod* ArtCompileDEX(
InvokeType invoke_type ATTRIBUTE_UNUSED,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
DexToDexCompilationLevel dex_to_dex_compilation_level) {
DCHECK(driver != nullptr);
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 0a00d45297..00c596d60e 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
#include "dex_file.h"
+#include "handle.h"
#include "invoke_type.h"
namespace art {
@@ -25,6 +26,10 @@ namespace art {
class CompiledMethod;
class CompilerDriver;
+namespace mirror {
+class ClassLoader;
+} // namespace mirror
+
namespace optimizer {
enum class DexToDexCompilationLevel {
@@ -40,7 +45,7 @@ CompiledMethod* ArtCompileDEX(CompilerDriver* driver,
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
DexToDexCompilationLevel dex_to_dex_compilation_level);
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index f296851ebf..582330611d 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -31,17 +31,12 @@
namespace art {
-inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa,
- const DexCompilationUnit* mUnit) {
- return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr();
-}
-
inline mirror::Class* CompilerDriver::ResolveClass(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index,
const DexCompilationUnit* mUnit) {
DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
- DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+ DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
*mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
@@ -56,7 +51,7 @@ inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
- DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+ DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
const DexFile::MethodId& referrer_method_id =
mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex());
return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
@@ -87,7 +82,7 @@ inline ArtField* CompilerDriver::ResolveField(
const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
uint32_t field_idx, bool is_static) {
- DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+ DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx,
is_static);
}
@@ -139,7 +134,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod(
ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change) {
- DCHECK_EQ(class_loader.Get(), GetClassLoader(soa, mUnit));
+ DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
ArtMethod* resolved_method =
check_incompatible_class_change
? mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 26c0818b85..52ffa55342 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -580,7 +580,7 @@ static void CompileMethod(Thread* self,
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level,
bool compilation_enabled,
@@ -621,9 +621,6 @@ static void CompileMethod(Thread* self,
// Look-up the ArtMethod associated with this code_item (if any)
// -- It is later used to lookup any [optimization] annotations for this method.
ScopedObjectAccess soa(self);
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader_handle(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(class_loader)));
// TODO: Lookup annotation from DexFile directly without resolving method.
ArtMethod* method =
@@ -631,7 +628,7 @@ static void CompileMethod(Thread* self,
dex_file,
method_idx,
dex_cache,
- class_loader_handle,
+ class_loader,
/* referrer */ nullptr,
invoke_type);
@@ -678,9 +675,14 @@ static void CompileMethod(Thread* self,
if (compile) {
// NOTE: if compiler declines to compile this method, it will return null.
- compiled_method = driver->GetCompiler()->Compile(code_item, access_flags, invoke_type,
- class_def_idx, method_idx, class_loader,
- dex_file, dex_cache);
+ compiled_method = driver->GetCompiler()->Compile(code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_cache);
}
if (compiled_method == nullptr &&
dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) {
@@ -727,12 +729,14 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t
uint32_t method_idx = method->GetDexMethodIndex();
uint32_t access_flags = method->GetAccessFlags();
InvokeType invoke_type = method->GetInvokeType();
- StackHandleScope<1> hs(self);
+ StackHandleScope<2> hs(self);
Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(method->GetDeclaringClass()->GetClassLoader()));
{
ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef<jobject> local_class_loader(
- soa.Env(), soa.AddLocalReference<jobject>(method->GetDeclaringClass()->GetClassLoader()));
+ soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
jclass_loader = soa.Env()->NewGlobalRef(local_class_loader.get());
// Find the dex_file
dex_file = method->GetDexFile();
@@ -766,7 +770,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t
invoke_type,
class_def_idx,
method_idx,
- jclass_loader,
+ class_loader,
*dex_file,
dex_to_dex_compilation_level,
true,
@@ -792,7 +796,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t
invoke_type,
class_def_idx,
method_idx,
- jclass_loader,
+ class_loader,
*dex_file,
dex_to_dex_compilation_level,
true,
@@ -1050,9 +1054,9 @@ bool CompilerDriver::IsMethodToCompile(const MethodReference& method_ref) const
}
bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const {
- if (profile_compilation_info_ == nullptr) {
- // If we miss profile information it means that we don't do a profile guided compilation.
- // Return true, and let the other filters decide if the method should be compiled.
+ if (!CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter())) {
+ // Use the compiler filter instead of the presence of profile_compilation_info_ since
+ // we may want to have full speed compilation along with profile based layout optimizations.
return true;
}
bool result = profile_compilation_info_->ContainsMethod(method_ref);
@@ -1067,22 +1071,30 @@ bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_r
class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
public:
- explicit ResolveCatchBlockExceptionsClassVisitor(
- std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve)
- : exceptions_to_resolve_(exceptions_to_resolve) {}
+ ResolveCatchBlockExceptionsClassVisitor() : classes_() {}
virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ classes_.push_back(c);
+ return true;
+ }
+
+ void FindExceptionTypesToResolve(
+ std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- for (auto& m : c->GetMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m);
+ for (ObjPtr<mirror::Class> klass : classes_) {
+ for (ArtMethod& method : klass->GetMethods(pointer_size)) {
+ FindExceptionTypesToResolveForMethod(&method, exceptions_to_resolve);
+ }
}
- return true;
}
private:
- void ResolveExceptionsForMethod(ArtMethod* method_handle)
+ void FindExceptionTypesToResolveForMethod(
+ ArtMethod* method,
+ std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
+ const DexFile::CodeItem* code_item = method->GetCodeItem();
if (code_item == nullptr) {
return; // native or abstract method
}
@@ -1102,9 +1114,9 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
dex::TypeIndex encoded_catch_handler_handlers_type_idx =
dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
// Add to set of types to resolve if not already in the dex cache resolved types
- if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
- exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
- method_handle->GetDexFile());
+ if (!method->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
+ exceptions_to_resolve->emplace(encoded_catch_handler_handlers_type_idx,
+ method->GetDexFile());
}
// ignore address associated with catch handler
DecodeUnsignedLeb128(&encoded_catch_handler_list);
@@ -1116,7 +1128,7 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor {
}
}
- std::set<std::pair<dex::TypeIndex, const DexFile*>>& exceptions_to_resolve_;
+ std::vector<ObjPtr<mirror::Class>> classes_;
};
class RecordImageClassesVisitor : public ClassVisitor {
@@ -1170,8 +1182,14 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) {
hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/lang/Throwable;")));
do {
unresolved_exception_types.clear();
- ResolveCatchBlockExceptionsClassVisitor visitor(unresolved_exception_types);
- class_linker->VisitClasses(&visitor);
+ {
+ // Thread suspension is not allowed while ResolveCatchBlockExceptionsClassVisitor
+ // is using a std::vector<ObjPtr<mirror::Class>>.
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ ResolveCatchBlockExceptionsClassVisitor visitor;
+ class_linker->VisitClasses(&visitor);
+ visitor.FindExceptionTypesToResolve(&unresolved_exception_types);
+ }
for (const auto& exception_type : unresolved_exception_types) {
dex::TypeIndex exception_type_idx = exception_type.first;
const DexFile* dex_file = exception_type.second;
@@ -1422,19 +1440,14 @@ void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodRefere
dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index);
}
-bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
- Handle<mirror::DexCache> dex_cache,
- dex::TypeIndex type_idx) {
- // Get type from dex cache assuming it was populated by the verifier
- mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
+bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+ ObjPtr<mirror::Class> resolved_class) {
if (resolved_class == nullptr) {
stats_->TypeNeedsAccessCheck();
return false; // Unknown class needs access checks.
}
- const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible.
if (!is_accessible) {
- mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
if (referrer_class == nullptr) {
stats_->TypeNeedsAccessCheck();
return false; // Incomplete referrer knowledge needs access check.
@@ -1451,12 +1464,9 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
return is_accessible;
}
-bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
- Handle<mirror::DexCache> dex_cache,
- dex::TypeIndex type_idx,
+bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+ ObjPtr<mirror::Class> resolved_class,
bool* finalizable) {
- // Get type from dex cache assuming it was populated by the verifier.
- mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
if (resolved_class == nullptr) {
stats_->TypeNeedsAccessCheck();
// Be conservative.
@@ -1464,10 +1474,8 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id
return false; // Unknown class needs access checks.
}
*finalizable = resolved_class->IsFinalizable();
- const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx);
bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible.
if (!is_accessible) {
- mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
if (referrer_class == nullptr) {
stats_->TypeNeedsAccessCheck();
return false; // Incomplete referrer knowledge needs access check.
@@ -1511,9 +1519,7 @@ ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx,
mirror::Class* referrer_class;
Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache());
{
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader_handle(
- hs.NewHandle(soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader())));
+ Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader();
resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false);
referrer_class = resolved_field != nullptr
? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr;
@@ -2585,10 +2591,18 @@ class CompileClassVisitor : public CompilationVisitor {
continue;
}
previous_direct_method_idx = method_idx;
- CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
- it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
- compilation_enabled, dex_cache);
+ CompileMethod(soa.Self(),
+ driver,
+ it.GetMethodCodeItem(),
+ it.GetMethodAccessFlags(),
+ it.GetMethodInvokeType(class_def),
+ class_def_index,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_to_dex_compilation_level,
+ compilation_enabled,
+ dex_cache);
it.Next();
}
// Compile virtual methods
@@ -2602,10 +2616,17 @@ class CompileClassVisitor : public CompilationVisitor {
continue;
}
previous_virtual_method_idx = method_idx;
- CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
- it.GetMethodInvokeType(class_def), class_def_index,
- method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
- compilation_enabled, dex_cache);
+ CompileMethod(soa.Self(),
+ driver, it.GetMethodCodeItem(),
+ it.GetMethodAccessFlags(),
+ it.GetMethodInvokeType(class_def),
+ class_def_index,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_to_dex_compilation_level,
+ compilation_enabled,
+ dex_cache);
it.Next();
}
DCHECK(!it.HasNext());
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 5b4c751c4a..1e5c43d833 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -187,16 +187,14 @@ class CompilerDriver {
REQUIRES(!requires_constructor_barrier_lock_);
// Are runtime access checks necessary in the compiled code?
- bool CanAccessTypeWithoutChecks(uint32_t referrer_idx,
- Handle<mirror::DexCache> dex_cache,
- dex::TypeIndex type_idx)
+ bool CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+ ObjPtr<mirror::Class> resolved_class)
REQUIRES_SHARED(Locks::mutator_lock_);
// Are runtime access and instantiable checks necessary in the code?
// out_is_finalizable is set to whether the type is finalizable.
- bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
- Handle<mirror::DexCache> dex_cache,
- dex::TypeIndex type_idx,
+ bool CanAccessInstantiableTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
+ ObjPtr<mirror::Class> resolved_class,
bool* out_is_finalizable)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -370,10 +368,6 @@ class CompilerDriver {
uint32_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa,
- const DexCompilationUnit* mUnit)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 1e4ca16844..97954f3c29 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -101,6 +101,7 @@ class CompilerDriverTest : public CommonCompilerTest {
};
// Disabled due to 10 second runtime on host
+// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
CompileAll(nullptr);
@@ -246,6 +247,11 @@ class CompilerDriverProfileTest : public CompilerDriverTest {
return &profile_info_;
}
+ CompilerFilter::Filter GetCompilerFilter() const OVERRIDE {
+ // Use a profile based filter.
+ return CompilerFilter::kSpeedProfile;
+ }
+
std::unordered_set<std::string> GetExpectedMethodsForClass(const std::string& clazz) {
if (clazz == "Main") {
return std::unordered_set<std::string>({
@@ -304,7 +310,6 @@ TEST_F(CompilerDriverProfileTest, ProfileGuidedCompilation) {
// Need to enable dex-file writability. Methods rejected to be compiled will run through the
// dex-to-dex compiler.
- ProfileCompilationInfo info;
for (const DexFile* dex_file : GetDexFiles(class_loader)) {
ASSERT_TRUE(dex_file->EnableWrite());
}
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 47b19297e5..7e8e812c4a 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -21,7 +21,7 @@
namespace art {
-DexCompilationUnit::DexCompilationUnit(jobject class_loader,
+DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
ClassLinker* class_linker,
const DexFile& dex_file,
const DexFile::CodeItem* code_item,
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 854927d747..24a9a5b653 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -34,7 +34,7 @@ class VerifiedMethod;
class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> {
public:
- DexCompilationUnit(jobject class_loader,
+ DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
ClassLinker* class_linker,
const DexFile& dex_file,
const DexFile::CodeItem* code_item,
@@ -44,7 +44,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> {
const VerifiedMethod* verified_method,
Handle<mirror::DexCache> dex_cache);
- jobject GetClassLoader() const {
+ Handle<mirror::ClassLoader> GetClassLoader() const {
return class_loader_;
}
@@ -113,7 +113,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> {
}
private:
- const jobject class_loader_;
+ const Handle<mirror::ClassLoader> class_loader_;
ClassLinker* const class_linker_;
@@ -125,7 +125,7 @@ class DexCompilationUnit : public DeletableArenaObject<kArenaAllocMisc> {
const uint32_t access_flags_;
const VerifiedMethod* verified_method_;
- Handle<mirror::DexCache> dex_cache_;
+ const Handle<mirror::DexCache> dex_cache_;
std::string symbol_;
};
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d2dd30d8e6..117d1131b5 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -940,9 +940,11 @@ void ImageWriter::PruneNonImageClasses() {
}
ObjPtr<mirror::DexCache> dex_cache = self->DecodeJObject(data.weak_root)->AsDexCache();
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
- Class* klass = dex_cache->GetResolvedType(dex::TypeIndex(i));
+ mirror::TypeDexCachePair pair =
+ dex_cache->GetResolvedTypes()[i].load(std::memory_order_relaxed);
+ mirror::Class* klass = pair.object.Read();
if (klass != nullptr && !KeepClass(klass)) {
- dex_cache->SetResolvedType(dex::TypeIndex(i), nullptr);
+ dex_cache->ClearResolvedType(dex::TypeIndex(pair.index));
}
}
ArtMethod** resolved_methods = dex_cache->GetResolvedMethods();
@@ -1922,8 +1924,7 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
// above comment for intern tables.
ClassTable temp_class_table;
temp_class_table.ReadFromMemory(class_table_memory_ptr);
- CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
- mirror::ClassLoader* class_loader = compile_app_image_ ? *class_loaders_.begin() : nullptr;
+ ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
CHECK_EQ(temp_class_table.NumZygoteClasses(class_loader),
table->NumNonZygoteClasses(class_loader) + table->NumZygoteClasses(class_loader));
UnbufferedRootVisitor visitor(&root_visitor, RootInfo(kRootUnknown));
@@ -2213,7 +2214,7 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache,
orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
ImageAddressVisitor(this));
}
- GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
+ mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
if (orig_types != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
NativeLocationInImage(orig_types),
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index cc7df1ce21..bdc7146632 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -51,8 +51,13 @@ class ImageSpace;
} // namespace space
} // namespace gc
+namespace mirror {
+class ClassLoader;
+} // namespace mirror
+
class ClassLoaderVisitor;
class ClassTable;
+class ImtConflictTable;
static constexpr int kInvalidFd = -1;
@@ -79,6 +84,11 @@ class ImageWriter FINAL {
return true;
}
+ ObjPtr<mirror::ClassLoader> GetClassLoader() {
+ CHECK_EQ(class_loaders_.size(), compile_app_image_ ? 1u : 0u);
+ return compile_app_image_ ? *class_loaders_.begin() : nullptr;
+ }
+
template <typename T>
T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
if (object == nullptr || IsInBootImage(object)) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 7c0cdbf270..0ea11255a8 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1060,6 +1060,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
size_t relative_offset) SHARED_LOCK_FUNCTION(Locks::mutator_lock_)
: OatDexMethodVisitor(writer, relative_offset),
+ class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
out_(out),
file_offset_(file_offset),
soa_(Thread::Current()),
@@ -1245,6 +1246,7 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
private:
+ ObjPtr<mirror::ClassLoader> class_loader_;
OutputStream* const out_;
const size_t file_offset_;
const ScopedObjectAccess soa_;
@@ -1303,10 +1305,12 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
}
mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(writer_->HasImage());
ObjPtr<mirror::DexCache> dex_cache = GetDexCache(patch.TargetTypeDexFile());
- mirror::Class* type = dex_cache->GetResolvedType(patch.TargetTypeIndex());
+ ObjPtr<mirror::Class> type =
+ ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_);
CHECK(type != nullptr);
- return type;
+ return type.Ptr();
}
mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index e4ad4222fb..3a4c9dbd16 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -54,7 +54,10 @@ class HGraphBuilder : public ValueObject {
compiler_driver_(driver),
compilation_stats_(compiler_stats),
block_builder_(graph, dex_file, code_item),
- ssa_builder_(graph, dex_compilation_unit->GetDexCache(), handles),
+ ssa_builder_(graph,
+ dex_compilation_unit->GetClassLoader(),
+ dex_compilation_unit->GetDexCache(),
+ handles),
instruction_builder_(graph,
&block_builder_,
&ssa_builder_,
@@ -80,10 +83,12 @@ class HGraphBuilder : public ValueObject {
code_item_(code_item),
dex_compilation_unit_(nullptr),
compiler_driver_(nullptr),
- null_dex_cache_(),
compilation_stats_(nullptr),
block_builder_(graph, nullptr, code_item),
- ssa_builder_(graph, null_dex_cache_, handles),
+ ssa_builder_(graph,
+ handles->NewHandle<mirror::ClassLoader>(nullptr),
+ handles->NewHandle<mirror::DexCache>(nullptr),
+ handles),
instruction_builder_(graph,
&block_builder_,
&ssa_builder_,
@@ -96,7 +101,7 @@ class HGraphBuilder : public ValueObject {
/* code_generator */ nullptr,
/* interpreter_metadata */ nullptr,
/* compiler_stats */ nullptr,
- null_dex_cache_,
+ handles->NewHandle<mirror::DexCache>(nullptr),
handles) {}
GraphAnalysisResult BuildGraph();
@@ -117,8 +122,6 @@ class HGraphBuilder : public ValueObject {
CompilerDriver* const compiler_driver_;
- ScopedNullHandle<mirror::DexCache> null_dex_cache_;
-
OptimizingCompilerStats* compilation_stats_;
HBasicBlockBuilder block_builder_;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 759a951d6b..7b84ef83cd 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -19,6 +19,7 @@
#include "arch/arm/instruction_set_features_arm.h"
#include "art_method.h"
#include "code_generator_utils.h"
+#include "common_arm.h"
#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
@@ -1132,10 +1133,6 @@ class ReadBarrierForRootSlowPathARM : public SlowPathCodeARM {
DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM);
};
-#undef __
-// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
-#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
-
inline Condition ARMCondition(IfCondition cond) {
switch (cond) {
case kCondEQ: return EQ;
@@ -1191,6 +1188,197 @@ inline Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
}
}
+inline Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
+ switch (op_kind) {
+ case HDataProcWithShifterOp::kASR: return ASR;
+ case HDataProcWithShifterOp::kLSL: return LSL;
+ case HDataProcWithShifterOp::kLSR: return LSR;
+ default:
+ LOG(FATAL) << "Unexpected op kind " << op_kind;
+ UNREACHABLE();
+ }
+}
+
+static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
+ Register out,
+ Register first,
+ const ShifterOperand& second,
+ CodeGeneratorARM* codegen) {
+ if (second.IsImmediate() && second.GetImmediate() == 0) {
+ const ShifterOperand in = kind == HInstruction::kAnd
+ ? ShifterOperand(0)
+ : ShifterOperand(first);
+
+ __ mov(out, in);
+ } else {
+ switch (kind) {
+ case HInstruction::kAdd:
+ __ add(out, first, second);
+ break;
+ case HInstruction::kAnd:
+ __ and_(out, first, second);
+ break;
+ case HInstruction::kOr:
+ __ orr(out, first, second);
+ break;
+ case HInstruction::kSub:
+ __ sub(out, first, second);
+ break;
+ case HInstruction::kXor:
+ __ eor(out, first, second);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected instruction kind: " << kind;
+ UNREACHABLE();
+ }
+ }
+}
+
+static void GenerateDataProc(HInstruction::InstructionKind kind,
+ const Location& out,
+ const Location& first,
+ const ShifterOperand& second_lo,
+ const ShifterOperand& second_hi,
+ CodeGeneratorARM* codegen) {
+ const Register first_hi = first.AsRegisterPairHigh<Register>();
+ const Register first_lo = first.AsRegisterPairLow<Register>();
+ const Register out_hi = out.AsRegisterPairHigh<Register>();
+ const Register out_lo = out.AsRegisterPairLow<Register>();
+
+ if (kind == HInstruction::kAdd) {
+ __ adds(out_lo, first_lo, second_lo);
+ __ adc(out_hi, first_hi, second_hi);
+ } else if (kind == HInstruction::kSub) {
+ __ subs(out_lo, first_lo, second_lo);
+ __ sbc(out_hi, first_hi, second_hi);
+ } else {
+ GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
+ GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
+ }
+}
+
+static ShifterOperand GetShifterOperand(Register rm, Shift shift, uint32_t shift_imm) {
+ return shift_imm == 0 ? ShifterOperand(rm) : ShifterOperand(rm, shift, shift_imm);
+}
+
+static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, CodeGeneratorARM* codegen) {
+ DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+ DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
+
+ const LocationSummary* const locations = instruction->GetLocations();
+ const uint32_t shift_value = instruction->GetShiftAmount();
+ const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+ const Location first = locations->InAt(0);
+ const Location second = locations->InAt(1);
+ const Location out = locations->Out();
+ const Register first_hi = first.AsRegisterPairHigh<Register>();
+ const Register first_lo = first.AsRegisterPairLow<Register>();
+ const Register out_hi = out.AsRegisterPairHigh<Register>();
+ const Register out_lo = out.AsRegisterPairLow<Register>();
+ const Register second_hi = second.AsRegisterPairHigh<Register>();
+ const Register second_lo = second.AsRegisterPairLow<Register>();
+ const Shift shift = ShiftFromOpKind(instruction->GetOpKind());
+
+ if (shift_value >= 32) {
+ if (shift == LSL) {
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ first_hi,
+ ShifterOperand(second_lo, LSL, shift_value - 32),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ first_lo,
+ ShifterOperand(0),
+ codegen);
+ } else if (shift == ASR) {
+ GenerateDataProc(kind,
+ out,
+ first,
+ GetShifterOperand(second_hi, ASR, shift_value - 32),
+ ShifterOperand(second_hi, ASR, 31),
+ codegen);
+ } else {
+ DCHECK_EQ(shift, LSR);
+ GenerateDataProc(kind,
+ out,
+ first,
+ GetShifterOperand(second_hi, LSR, shift_value - 32),
+ ShifterOperand(0),
+ codegen);
+ }
+ } else {
+ DCHECK_GT(shift_value, 1U);
+ DCHECK_LT(shift_value, 32U);
+
+ if (shift == LSL) {
+ // We are not doing this for HInstruction::kAdd because the output will require
+ // Location::kOutputOverlap; not applicable to other cases.
+ if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ first_hi,
+ ShifterOperand(second_hi, LSL, shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ out_hi,
+ ShifterOperand(second_lo, LSR, 32 - shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ first_lo,
+ ShifterOperand(second_lo, LSL, shift_value),
+ codegen);
+ } else {
+ __ Lsl(IP, second_hi, shift_value);
+ __ orr(IP, IP, ShifterOperand(second_lo, LSR, 32 - shift_value));
+ GenerateDataProc(kind,
+ out,
+ first,
+ ShifterOperand(second_lo, LSL, shift_value),
+ ShifterOperand(IP),
+ codegen);
+ }
+ } else {
+ DCHECK(shift == ASR || shift == LSR);
+
+ // We are not doing this for HInstruction::kAdd because the output will require
+ // Location::kOutputOverlap; not applicable to other cases.
+ if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ first_lo,
+ ShifterOperand(second_lo, LSR, shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ out_lo,
+ ShifterOperand(second_hi, LSL, 32 - shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ first_hi,
+ ShifterOperand(second_hi, shift, shift_value),
+ codegen);
+ } else {
+ __ Lsr(IP, second_lo, shift_value);
+ __ orr(IP, IP, ShifterOperand(second_hi, LSL, 32 - shift_value));
+ GenerateDataProc(kind,
+ out,
+ first,
+ ShifterOperand(IP),
+ ShifterOperand(second_hi, shift, shift_value),
+ codegen);
+ }
+ }
+ }
+}
+
+#undef __
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT
+
void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << Register(reg);
}
@@ -6709,6 +6897,63 @@ void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight*
}
}
+void LocationsBuilderARM::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* instruction) {
+ DCHECK(instruction->GetType() == Primitive::kPrimInt ||
+ instruction->GetType() == Primitive::kPrimLong);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ const bool overlap = instruction->GetType() == Primitive::kPrimLong &&
+ HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(),
+ overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* instruction) {
+ const LocationSummary* const locations = instruction->GetLocations();
+ const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+ const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
+ const Location left = locations->InAt(0);
+ const Location right = locations->InAt(1);
+ const Location out = locations->Out();
+
+ if (instruction->GetType() == Primitive::kPrimInt) {
+ DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind));
+
+ const Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong
+ ? right.AsRegisterPairLow<Register>()
+ : right.AsRegister<Register>();
+
+ GenerateDataProcInstruction(kind,
+ out.AsRegister<Register>(),
+ left.AsRegister<Register>(),
+ ShifterOperand(second,
+ ShiftFromOpKind(op_kind),
+ instruction->GetShiftAmount()),
+ codegen_);
+ } else {
+ DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+
+ if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
+ const Register second = right.AsRegister<Register>();
+
+ DCHECK_NE(out.AsRegisterPairLow<Register>(), second);
+ GenerateDataProc(kind,
+ out,
+ left,
+ ShifterOperand(second),
+ ShifterOperand(second, ASR, 31),
+ codegen_);
+ } else {
+ GenerateLongDataProc(instruction, codegen_);
+ }
+ }
+}
+
void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) {
// Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
if (value == 0xffffffffu) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index e6032d2381..edccbd4904 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2277,8 +2277,8 @@ void InstructionCodeGeneratorARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRigh
}
}
-void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp(
- HArm64DataProcWithShifterOp* instruction) {
+void LocationsBuilderARM64::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* instruction) {
DCHECK(instruction->GetType() == Primitive::kPrimInt ||
instruction->GetType() == Primitive::kPrimLong);
LocationSummary* locations =
@@ -2292,8 +2292,8 @@ void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp(
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
-void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp(
- HArm64DataProcWithShifterOp* instruction) {
+void InstructionCodeGeneratorARM64::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* instruction) {
Primitive::Type type = instruction->GetType();
HInstruction::InstructionKind kind = instruction->GetInstrKind();
DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
@@ -2302,21 +2302,20 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp(
if (kind != HInstruction::kNeg) {
left = InputRegisterAt(instruction, 0);
}
- // If this `HArm64DataProcWithShifterOp` was created by merging a type conversion as the
+ // If this `HDataProcWithShifterOp` was created by merging a type conversion as the
// shifter operand operation, the IR generating `right_reg` (input to the type
// conversion) can have a different type from the current instruction's type,
// so we manually indicate the type.
Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type);
- int64_t shift_amount = instruction->GetShiftAmount() &
- (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance);
-
Operand right_operand(0);
- HArm64DataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
- if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind)) {
+ HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
+ if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
right_operand = Operand(right_reg, helpers::ExtendFromOpKind(op_kind));
} else {
- right_operand = Operand(right_reg, helpers::ShiftFromOpKind(op_kind), shift_amount);
+ right_operand = Operand(right_reg,
+ helpers::ShiftFromOpKind(op_kind),
+ instruction->GetShiftAmount());
}
// Logical binary operations do not support extension operations in the
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 5c4ca5bc17..6bfbe4a9c9 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -1216,6 +1216,17 @@ inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
}
}
+inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
+ switch (op_kind) {
+ case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
+ case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
+ case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
+ default:
+ LOG(FATAL) << "Unexpected op kind " << op_kind;
+ UNREACHABLE();
+ }
+}
+
void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
stream << vixl32::Register(reg);
}
@@ -1260,6 +1271,185 @@ size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATT
return 0;
}
+static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
+ vixl32::Register out,
+ vixl32::Register first,
+ const Operand& second,
+ CodeGeneratorARMVIXL* codegen) {
+ if (second.IsImmediate() && second.GetImmediate() == 0) {
+ const Operand in = kind == HInstruction::kAnd
+ ? Operand(0)
+ : Operand(first);
+
+ __ Mov(out, in);
+ } else {
+ switch (kind) {
+ case HInstruction::kAdd:
+ __ Add(out, first, second);
+ break;
+ case HInstruction::kAnd:
+ __ And(out, first, second);
+ break;
+ case HInstruction::kOr:
+ __ Orr(out, first, second);
+ break;
+ case HInstruction::kSub:
+ __ Sub(out, first, second);
+ break;
+ case HInstruction::kXor:
+ __ Eor(out, first, second);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected instruction kind: " << kind;
+ UNREACHABLE();
+ }
+ }
+}
+
+static void GenerateDataProc(HInstruction::InstructionKind kind,
+ const Location& out,
+ const Location& first,
+ const Operand& second_lo,
+ const Operand& second_hi,
+ CodeGeneratorARMVIXL* codegen) {
+ const vixl32::Register first_hi = HighRegisterFrom(first);
+ const vixl32::Register first_lo = LowRegisterFrom(first);
+ const vixl32::Register out_hi = HighRegisterFrom(out);
+ const vixl32::Register out_lo = LowRegisterFrom(out);
+
+ if (kind == HInstruction::kAdd) {
+ __ Adds(out_lo, first_lo, second_lo);
+ __ Adc(out_hi, first_hi, second_hi);
+ } else if (kind == HInstruction::kSub) {
+ __ Subs(out_lo, first_lo, second_lo);
+ __ Sbc(out_hi, first_hi, second_hi);
+ } else {
+ GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
+ GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
+ }
+}
+
+static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
+ return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
+}
+
+static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
+ CodeGeneratorARMVIXL* codegen) {
+ DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+ DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
+
+ const LocationSummary* const locations = instruction->GetLocations();
+ const uint32_t shift_value = instruction->GetShiftAmount();
+ const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+ const Location first = locations->InAt(0);
+ const Location second = locations->InAt(1);
+ const Location out = locations->Out();
+ const vixl32::Register first_hi = HighRegisterFrom(first);
+ const vixl32::Register first_lo = LowRegisterFrom(first);
+ const vixl32::Register out_hi = HighRegisterFrom(out);
+ const vixl32::Register out_lo = LowRegisterFrom(out);
+ const vixl32::Register second_hi = HighRegisterFrom(second);
+ const vixl32::Register second_lo = LowRegisterFrom(second);
+ const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
+
+ if (shift_value >= 32) {
+ if (shift == ShiftType::LSL) {
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ first_hi,
+ Operand(second_lo, ShiftType::LSL, shift_value - 32),
+ codegen);
+ GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
+ } else if (shift == ShiftType::ASR) {
+ GenerateDataProc(kind,
+ out,
+ first,
+ GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
+ Operand(second_hi, ShiftType::ASR, 31),
+ codegen);
+ } else {
+ DCHECK_EQ(shift, ShiftType::LSR);
+ GenerateDataProc(kind,
+ out,
+ first,
+ GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
+ 0,
+ codegen);
+ }
+ } else {
+ DCHECK_GT(shift_value, 1U);
+ DCHECK_LT(shift_value, 32U);
+
+ UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
+
+ if (shift == ShiftType::LSL) {
+ // We are not doing this for HInstruction::kAdd because the output will require
+ // Location::kOutputOverlap; not applicable to other cases.
+ if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ first_hi,
+ Operand(second_hi, ShiftType::LSL, shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ out_hi,
+ Operand(second_lo, ShiftType::LSR, 32 - shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ first_lo,
+ Operand(second_lo, ShiftType::LSL, shift_value),
+ codegen);
+ } else {
+ const vixl32::Register temp = temps.Acquire();
+
+ __ Lsl(temp, second_hi, shift_value);
+ __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
+ GenerateDataProc(kind,
+ out,
+ first,
+ Operand(second_lo, ShiftType::LSL, shift_value),
+ temp,
+ codegen);
+ }
+ } else {
+ DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
+
+ // We are not doing this for HInstruction::kAdd because the output will require
+ // Location::kOutputOverlap; not applicable to other cases.
+ if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ first_lo,
+ Operand(second_lo, ShiftType::LSR, shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_lo,
+ out_lo,
+ Operand(second_hi, ShiftType::LSL, 32 - shift_value),
+ codegen);
+ GenerateDataProcInstruction(kind,
+ out_hi,
+ first_hi,
+ Operand(second_hi, shift, shift_value),
+ codegen);
+ } else {
+ const vixl32::Register temp = temps.Acquire();
+
+ __ Lsr(temp, second_lo, shift_value);
+ __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
+ GenerateDataProc(kind,
+ out,
+ first,
+ temp,
+ Operand(second_hi, shift, shift_value),
+ codegen);
+ }
+ }
+ }
+}
+
#undef __
CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
@@ -6781,6 +6971,60 @@ void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRi
}
}
+void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* instruction) {
+ DCHECK(instruction->GetType() == Primitive::kPrimInt ||
+ instruction->GetType() == Primitive::kPrimLong);
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ const bool overlap = instruction->GetType() == Primitive::kPrimLong &&
+ HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(),
+ overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* instruction) {
+ const LocationSummary* const locations = instruction->GetLocations();
+ const HInstruction::InstructionKind kind = instruction->GetInstrKind();
+ const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
+
+ if (instruction->GetType() == Primitive::kPrimInt) {
+ DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind));
+
+ const vixl32::Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong
+ ? LowRegisterFrom(locations->InAt(1))
+ : InputRegisterAt(instruction, 1);
+
+ GenerateDataProcInstruction(kind,
+ OutputRegister(instruction),
+ InputRegisterAt(instruction, 0),
+ Operand(second,
+ ShiftFromOpKind(op_kind),
+ instruction->GetShiftAmount()),
+ codegen_);
+ } else {
+ DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong);
+
+ if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
+ const vixl32::Register second = InputRegisterAt(instruction, 1);
+
+ DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
+ GenerateDataProc(kind,
+ locations->Out(),
+ locations->InAt(0),
+ second,
+ Operand(second, ShiftType::ASR, 31),
+ codegen_);
+ } else {
+ GenerateLongDataProc(instruction, codegen_);
+ }
+ }
+}
+
// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
vixl32::Register first,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 09612c8dbf..b779aed763 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5262,7 +5262,7 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
// Branch cases into compressed and uncompressed for each index's type.
uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
NearLabel done, not_compressed;
- __ testl(Address(obj, count_offset), Immediate(1));
+ __ testb(Address(obj, count_offset), Immediate(1));
codegen_->MaybeRecordImplicitNullCheck(instruction);
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 0879992e32..179bf6d3d1 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4720,7 +4720,7 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
// Branch cases into compressed and uncompressed for each index's type.
uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
NearLabel done, not_compressed;
- __ testl(Address(obj, count_offset), Immediate(1));
+ __ testb(Address(obj, count_offset), Immediate(1));
codegen_->MaybeRecordImplicitNullCheck(instruction);
static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
"Expecting 0=compressed, 1=uncompressed");
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index ecb86875d6..e184745520 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
+#include "instruction_simplifier_shared.h"
#include "debug/dwarf/register.h"
#include "locations.h"
#include "nodes.h"
@@ -29,6 +30,9 @@
#pragma GCC diagnostic pop
namespace art {
+
+using helpers::HasShifterOperand;
+
namespace arm {
namespace helpers {
@@ -218,6 +222,14 @@ inline Location LocationFrom(const vixl::aarch32::SRegister& low,
return Location::FpuRegisterPairLocation(low.GetCode(), high.GetCode());
}
+inline bool ShifterOperandSupportsExtension(HInstruction* instruction) {
+ DCHECK(HasShifterOperand(instruction, kArm));
+ // TODO: HAdd applied to the other integral types could make use of
+ // the SXTAB, SXTAH, UXTAB and UXTAH instructions.
+ return instruction->GetType() == Primitive::kPrimLong &&
+ (instruction->IsAdd() || instruction->IsSub());
+}
+
} // namespace helpers
} // namespace arm
} // namespace art
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 93ea090583..d3f431e327 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
#include "code_generator.h"
+#include "instruction_simplifier_shared.h"
#include "locations.h"
#include "nodes.h"
#include "utils/arm64/assembler_arm64.h"
@@ -31,6 +32,10 @@
#pragma GCC diagnostic pop
namespace art {
+
+using helpers::CanFitInShifterOperand;
+using helpers::HasShifterOperand;
+
namespace arm64 {
namespace helpers {
@@ -290,11 +295,11 @@ inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers,
return true;
}
-inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
+inline vixl::aarch64::Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
switch (op_kind) {
- case HArm64DataProcWithShifterOp::kASR: return vixl::aarch64::ASR;
- case HArm64DataProcWithShifterOp::kLSL: return vixl::aarch64::LSL;
- case HArm64DataProcWithShifterOp::kLSR: return vixl::aarch64::LSR;
+ case HDataProcWithShifterOp::kASR: return vixl::aarch64::ASR;
+ case HDataProcWithShifterOp::kLSL: return vixl::aarch64::LSL;
+ case HDataProcWithShifterOp::kLSR: return vixl::aarch64::LSR;
default:
LOG(FATAL) << "Unexpected op kind " << op_kind;
UNREACHABLE();
@@ -302,14 +307,14 @@ inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind
}
}
-inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) {
+inline vixl::aarch64::Extend ExtendFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
switch (op_kind) {
- case HArm64DataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB;
- case HArm64DataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH;
- case HArm64DataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW;
- case HArm64DataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB;
- case HArm64DataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH;
- case HArm64DataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW;
+ case HDataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB;
+ case HDataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH;
+ case HDataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW;
+ case HDataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB;
+ case HDataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH;
+ case HDataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW;
default:
LOG(FATAL) << "Unexpected op kind " << op_kind;
UNREACHABLE();
@@ -317,31 +322,8 @@ inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKin
}
}
-inline bool CanFitInShifterOperand(HInstruction* instruction) {
- if (instruction->IsTypeConversion()) {
- HTypeConversion* conversion = instruction->AsTypeConversion();
- Primitive::Type result_type = conversion->GetResultType();
- Primitive::Type input_type = conversion->GetInputType();
- // We don't expect to see the same type as input and result.
- return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) &&
- (result_type != input_type);
- } else {
- return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) ||
- (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) ||
- (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant());
- }
-}
-
-inline bool HasShifterOperand(HInstruction* instr) {
- // `neg` instructions are an alias of `sub` using the zero register as the
- // first register input.
- bool res = instr->IsAdd() || instr->IsAnd() || instr->IsNeg() ||
- instr->IsOr() || instr->IsSub() || instr->IsXor();
- return res;
-}
-
inline bool ShifterOperandSupportsExtension(HInstruction* instruction) {
- DCHECK(HasShifterOperand(instruction));
+ DCHECK(HasShifterOperand(instruction, kArm64));
// Although the `neg` instruction is an alias of the `sub` instruction, `HNeg`
// does *not* support extension. This is because the `extended register` form
// of the `sub` instruction interprets the left register with code 31 as the
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index f6fba883bd..2bf5c53e17 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -511,12 +511,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE {
StartAttributeStream("kind") << instruction->GetOpKind();
}
-#endif
-#ifdef ART_ENABLE_CODEGEN_arm64
- void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE {
+ void VisitDataProcWithShifterOp(HDataProcWithShifterOp* instruction) OVERRIDE {
StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind();
- if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
+ if (HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
StartAttributeStream("shift") << instruction->GetShiftAmount();
}
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index e012a4287f..8c73f1d036 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -192,9 +192,9 @@ static uint32_t FindMethodIndexIn(ArtMethod* method,
}
static dex::TypeIndex FindClassIndexIn(mirror::Class* cls,
- const DexFile& dex_file,
- Handle<mirror::DexCache> dex_cache)
+ const DexCompilationUnit& compilation_unit)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ const DexFile& dex_file = *compilation_unit.GetDexFile();
dex::TypeIndex index;
if (cls->GetDexCache() == nullptr) {
DCHECK(cls->IsArrayClass()) << cls->PrettyClass();
@@ -203,22 +203,19 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls,
DCHECK(cls->IsProxyClass()) << cls->PrettyClass();
// TODO: deal with proxy classes.
} else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
- DCHECK_EQ(cls->GetDexCache(), dex_cache.Get());
+ DCHECK_EQ(cls->GetDexCache(), compilation_unit.GetDexCache().Get());
index = cls->GetDexTypeIndex();
- // Update the dex cache to ensure the class is in. The generated code will
- // consider it is. We make it safe by updating the dex cache, as other
- // dex files might also load the class, and there is no guarantee the dex
- // cache of the dex file of the class will be updated.
- if (dex_cache->GetResolvedType(index) == nullptr) {
- dex_cache->SetResolvedType(index, cls);
- }
} else {
index = cls->FindTypeIndexInOtherDexFile(dex_file);
- // We cannot guarantee the entry in the dex cache will resolve to the same class,
+ // We cannot guarantee the entry will resolve to the same class,
// as there may be different class loaders. So only return the index if it's
- // the right class in the dex cache already.
- if (index.IsValid() && dex_cache->GetResolvedType(index) != cls) {
- index = dex::TypeIndex::Invalid();
+ // the right class already resolved with the class loader.
+ if (index.IsValid()) {
+ ObjPtr<mirror::Class> resolved = ClassLinker::LookupResolvedType(
+ index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
+ if (resolved != cls) {
+ index = dex::TypeIndex::Invalid();
+ }
}
}
@@ -445,9 +442,8 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
<< invoke_instruction->DebugName();
- const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
dex::TypeIndex class_index = FindClassIndexIn(
- GetMonomorphicType(classes), caller_dex_file, caller_compilation_unit_.GetDexCache());
+ GetMonomorphicType(classes), caller_compilation_unit_);
if (!class_index.IsValid()) {
VLOG(compiler) << "Call to " << ArtMethod::PrettyMethod(resolved_method)
<< " from inline cache is not inlined because its class is not"
@@ -490,6 +486,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
// Run type propagation to get the guard typed, and eventually propagate the
// type of the receiver.
ReferenceTypePropagation rtp_fixup(graph_,
+ outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
handles_,
/* is_first_run */ false);
@@ -583,7 +580,6 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
PointerSize pointer_size = class_linker->GetImagePointerSize();
- const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
bool all_targets_inlined = true;
bool one_target_inlined = false;
@@ -605,8 +601,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
HInstruction* cursor = invoke_instruction->GetPrevious();
HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- dex::TypeIndex class_index = FindClassIndexIn(
- handle.Get(), caller_dex_file, caller_compilation_unit_.GetDexCache());
+ dex::TypeIndex class_index = FindClassIndexIn(handle.Get(), caller_compilation_unit_);
HInstruction* return_replacement = nullptr;
if (!class_index.IsValid() ||
!TryBuildAndInline(invoke_instruction,
@@ -662,6 +657,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
// Run type propagation to get the guards typed.
ReferenceTypePropagation rtp_fixup(graph_,
+ outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
handles_,
/* is_first_run */ false);
@@ -855,6 +851,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(
// Run type propagation to get the guard typed.
ReferenceTypePropagation rtp_fixup(graph_,
+ outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
handles_,
/* is_first_run */ false);
@@ -923,6 +920,7 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
// Actual return value has a more specific type than the method's declared
// return type. Run RTP again on the outer graph to propagate it.
ReferenceTypePropagation(graph_,
+ outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
handles_,
/* is_first_run */ false).Run();
@@ -1175,7 +1173,11 @@ HInstanceFieldGet* HInliner::CreateInstanceFieldGet(Handle<mirror::DexCache> dex
/* dex_pc */ 0);
if (iget->GetType() == Primitive::kPrimNot) {
// Use the same dex_cache that we used for field lookup as the hint_dex_cache.
- ReferenceTypePropagation rtp(graph_, dex_cache, handles_, /* is_first_run */ false);
+ ReferenceTypePropagation rtp(graph_,
+ outer_compilation_unit_.GetClassLoader(),
+ dex_cache,
+ handles_,
+ /* is_first_run */ false);
rtp.Visit(iget);
}
return iget;
@@ -1221,7 +1223,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
resolved_method->GetDeclaringClass()->GetClassLoader()));
DexCompilationUnit dex_compilation_unit(
- class_loader.ToJObject(),
+ class_loader,
class_linker,
callee_dex_file,
code_item,
@@ -1338,6 +1340,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
// are more specific than the declared ones, run RTP again on the inner graph.
if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
ReferenceTypePropagation(callee_graph,
+ outer_compilation_unit_.GetClassLoader(),
dex_compilation_unit.GetDexCache(),
handles_,
/* is_first_run */ false).Run();
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 3374e42955..c60f6e5393 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -669,11 +669,10 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
+ StackHandleScope<2> hs(soa.Self());
ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
// We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
// at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
@@ -1260,9 +1259,7 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio
static mirror::Class* GetClassFrom(CompilerDriver* driver,
const DexCompilationUnit& compilation_unit) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(compilation_unit.GetClassLoader())));
+ Handle<mirror::ClassLoader> class_loader = compilation_unit.GetClassLoader();
Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache();
return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit);
@@ -1278,10 +1275,9 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const {
bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) const {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<3> hs(soa.Self());
+ StackHandleScope<2> hs(soa.Self());
Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass()));
@@ -1317,8 +1313,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static,
StackHandleScope<2> hs(soa.Self());
ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(),
@@ -1635,10 +1630,8 @@ static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls)
HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(dex_compilation_unit_->GetClassLoader())));
+ Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
Handle<mirror::Class> klass = handles_->NewHandle(compiler_driver_->ResolveClass(
soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_));
@@ -1722,17 +1715,9 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
}
}
-bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index,
- Handle<mirror::DexCache> dex_cache,
- bool* finalizable) const {
- return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
- dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable);
-}
-
bool HInstructionBuilder::NeedsAccessCheck(dex::TypeIndex type_index, bool* finalizable) const {
- ScopedObjectAccess soa(Thread::Current());
- Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache();
- return NeedsAccessCheck(type_index, dex_cache, finalizable);
+ return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks(
+ LookupReferrerClass(), LookupResolvedType(type_index, *dex_compilation_unit_), finalizable);
}
bool HInstructionBuilder::CanDecodeQuickenedInfo() const {
@@ -2772,4 +2757,18 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction,
return true;
} // NOLINT(readability/fn_size)
+ObjPtr<mirror::Class> HInstructionBuilder::LookupResolvedType(
+ dex::TypeIndex type_index,
+ const DexCompilationUnit& compilation_unit) const {
+ return ClassLinker::LookupResolvedType(
+ type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get());
+}
+
+ObjPtr<mirror::Class> HInstructionBuilder::LookupReferrerClass() const {
+ // TODO: Cache the result in a Handle<mirror::Class>.
+ const DexFile::MethodId& method_id =
+ dex_compilation_unit_->GetDexFile()->GetMethodId(dex_compilation_unit_->GetDexMethodIndex());
+ return LookupResolvedType(method_id.class_idx_, *dex_compilation_unit_);
+}
+
} // namespace art
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 3bb680ce44..e735a0c46d 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -106,11 +106,8 @@ class HInstructionBuilder : public ValueObject {
// Returns whether the current method needs access check for the type.
// Output parameter finalizable is set to whether the type is finalizable.
- bool NeedsAccessCheck(dex::TypeIndex type_index,
- Handle<mirror::DexCache> dex_cache,
- /*out*/bool* finalizable) const
+ bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const
REQUIRES_SHARED(Locks::mutator_lock_);
- bool NeedsAccessCheck(dex::TypeIndex type_index, /*out*/bool* finalizable) const;
template<typename T>
void Unop_12x(const Instruction& instruction, Primitive::Type type, uint32_t dex_pc);
@@ -300,6 +297,12 @@ class HInstructionBuilder : public ValueObject {
// be found.
ArtField* ResolveField(uint16_t field_idx, bool is_static, bool is_put);
+ ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index,
+ const DexCompilationUnit& compilation_unit) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_);
+
ArenaAllocator* const arena_;
HGraph* const graph_;
VariableSizedHandleScope* handles_;
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 56e4c7a9c2..5f5e29b024 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -15,23 +15,124 @@
*/
#include "code_generator.h"
+#include "common_arm.h"
#include "instruction_simplifier_arm.h"
#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
+#include "nodes.h"
namespace art {
+
+using helpers::CanFitInShifterOperand;
+using helpers::HasShifterOperand;
+
namespace arm {
-void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) {
- if (TryCombineMultiplyAccumulate(instruction, kArm)) {
+using helpers::ShifterOperandSupportsExtension;
+
+bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* use,
+ HInstruction* bitfield_op,
+ bool do_merge) {
+ DCHECK(HasShifterOperand(use, kArm));
+ DCHECK(use->IsBinaryOperation());
+ DCHECK(CanFitInShifterOperand(bitfield_op));
+ DCHECK(!bitfield_op->HasEnvironmentUses());
+
+ Primitive::Type type = use->GetType();
+ if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
+ return false;
+ }
+
+ HInstruction* left = use->InputAt(0);
+ HInstruction* right = use->InputAt(1);
+ DCHECK(left == bitfield_op || right == bitfield_op);
+
+ if (left == right) {
+ // TODO: Handle special transformations in this situation?
+ // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`?
+ // Or should this be part of a separate transformation logic?
+ return false;
+ }
+
+ bool is_commutative = use->AsBinaryOperation()->IsCommutative();
+ HInstruction* other_input;
+ if (bitfield_op == right) {
+ other_input = left;
+ } else {
+ if (is_commutative) {
+ other_input = right;
+ } else {
+ return false;
+ }
+ }
+
+ HDataProcWithShifterOp::OpKind op_kind;
+ int shift_amount = 0;
+
+ HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
+ shift_amount &= use->GetType() == Primitive::kPrimInt
+ ? kMaxIntShiftDistance
+ : kMaxLongShiftDistance;
+
+ if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
+ if (!ShifterOperandSupportsExtension(use)) {
+ return false;
+ }
+ // Shift by 1 is a special case that results in the same number and type of instructions
+ // as this simplification, but potentially shorter code.
+ } else if (type == Primitive::kPrimLong && shift_amount == 1) {
+ return false;
+ }
+
+ if (do_merge) {
+ HDataProcWithShifterOp* alu_with_op =
+ new (GetGraph()->GetArena()) HDataProcWithShifterOp(use,
+ other_input,
+ bitfield_op->InputAt(0),
+ op_kind,
+ shift_amount,
+ use->GetDexPc());
+ use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
+ if (bitfield_op->GetUses().empty()) {
+ bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
+ }
RecordSimplification();
}
+
+ return true;
}
-void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) {
- if (TryMergeNegatedInput(instruction)) {
- RecordSimplification();
+// Merge a bitfield move instruction into its uses if it can be merged in all of them.
+bool InstructionSimplifierArmVisitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) {
+ DCHECK(CanFitInShifterOperand(bitfield_op));
+
+ if (bitfield_op->HasEnvironmentUses()) {
+ return false;
+ }
+
+ const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
+
+ // Check whether we can merge the instruction in all its users' shifter operand.
+ for (const HUseListNode<HInstruction*>& use : uses) {
+ HInstruction* user = use.GetUser();
+ if (!HasShifterOperand(user, kArm)) {
+ return false;
+ }
+ if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
+ return false;
+ }
}
+
+ // Merge the instruction into its uses.
+ for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
+ HInstruction* user = it->GetUser();
+ // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand().
+ ++it;
+ bool merged = MergeIntoShifterOperand(user, bitfield_op);
+ DCHECK(merged);
+ }
+
+ return true;
}
void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) {
@@ -89,5 +190,49 @@ void InstructionSimplifierArmVisitor::VisitArraySet(HArraySet* instruction) {
}
}
+void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) {
+ if (TryCombineMultiplyAccumulate(instruction, kArm)) {
+ RecordSimplification();
+ }
+}
+
+void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) {
+ if (TryMergeNegatedInput(instruction)) {
+ RecordSimplification();
+ }
+}
+
+void InstructionSimplifierArmVisitor::VisitShl(HShl* instruction) {
+ if (instruction->InputAt(1)->IsConstant()) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
+void InstructionSimplifierArmVisitor::VisitShr(HShr* instruction) {
+ if (instruction->InputAt(1)->IsConstant()) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
+void InstructionSimplifierArmVisitor::VisitTypeConversion(HTypeConversion* instruction) {
+ Primitive::Type result_type = instruction->GetResultType();
+ Primitive::Type input_type = instruction->GetInputType();
+
+ if (input_type == result_type) {
+ // We let the arch-independent code handle this.
+ return;
+ }
+
+ if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
+void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) {
+ if (instruction->InputAt(1)->IsConstant()) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
index 9b54511340..e2ed257777 100644
--- a/compiler/optimizing/instruction_simplifier_arm.h
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -35,11 +35,41 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor {
}
}
- void VisitMul(HMul* instruction) OVERRIDE;
- void VisitOr(HOr* instruction) OVERRIDE;
+ bool TryMergeIntoUsersShifterOperand(HInstruction* instruction);
+ bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge);
+ bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
+ return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false);
+ }
+ bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
+ DCHECK(CanMergeIntoShifterOperand(use, bitfield_op));
+ return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true);
+ }
+
+ /**
+ * This simplifier uses a special-purpose BB visitor.
+ * (1) No need to visit Phi nodes.
+ * (2) Since statements can be removed in a "forward" fashion,
+ * the visitor should test if each statement is still there.
+ */
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE {
+ // TODO: fragile iteration, provide more robust iterators?
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsInBlock()) {
+ instruction->Accept(this);
+ }
+ }
+ }
+
void VisitAnd(HAnd* instruction) OVERRIDE;
void VisitArrayGet(HArrayGet* instruction) OVERRIDE;
void VisitArraySet(HArraySet* instruction) OVERRIDE;
+ void VisitMul(HMul* instruction) OVERRIDE;
+ void VisitOr(HOr* instruction) OVERRIDE;
+ void VisitShl(HShl* instruction) OVERRIDE;
+ void VisitShr(HShr* instruction) OVERRIDE;
+ void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
+ void VisitUShr(HUShr* instruction) OVERRIDE;
OptimizingCompilerStats* stats_;
};
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 6d107d571f..73b7b2bd95 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -22,16 +22,18 @@
#include "mirror/string.h"
namespace art {
-namespace arm64 {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
+
+namespace arm64 {
+
using helpers::ShifterOperandSupportsExtension;
bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use,
HInstruction* bitfield_op,
bool do_merge) {
- DCHECK(HasShifterOperand(use));
+ DCHECK(HasShifterOperand(use, kArm64));
DCHECK(use->IsBinaryOperation() || use->IsNeg());
DCHECK(CanFitInShifterOperand(bitfield_op));
DCHECK(!bitfield_op->HasEnvironmentUses());
@@ -72,23 +74,22 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction*
}
}
- HArm64DataProcWithShifterOp::OpKind op_kind;
+ HDataProcWithShifterOp::OpKind op_kind;
int shift_amount = 0;
- HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
+ HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
- if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind) &&
- !ShifterOperandSupportsExtension(use)) {
+ if (HDataProcWithShifterOp::IsExtensionOp(op_kind) && !ShifterOperandSupportsExtension(use)) {
return false;
}
if (do_merge) {
- HArm64DataProcWithShifterOp* alu_with_op =
- new (GetGraph()->GetArena()) HArm64DataProcWithShifterOp(use,
- other_input,
- bitfield_op->InputAt(0),
- op_kind,
- shift_amount,
- use->GetDexPc());
+ HDataProcWithShifterOp* alu_with_op =
+ new (GetGraph()->GetArena()) HDataProcWithShifterOp(use,
+ other_input,
+ bitfield_op->InputAt(0),
+ op_kind,
+ shift_amount,
+ use->GetDexPc());
use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
if (bitfield_op->GetUses().empty()) {
bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
@@ -112,7 +113,7 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc
// Check whether we can merge the instruction in all its users' shifter operand.
for (const HUseListNode<HInstruction*>& use : uses) {
HInstruction* user = use.GetUser();
- if (!HasShifterOperand(user)) {
+ if (!HasShifterOperand(user, kArm64)) {
return false;
}
if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index d4cb1f14b7..65654f50f4 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -40,11 +40,11 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor {
HInstruction* bitfield_op,
bool do_merge);
bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
- return TryMergeIntoShifterOperand(use, bitfield_op, false);
+ return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false);
}
bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) {
DCHECK(CanMergeIntoShifterOperand(use, bitfield_op));
- return TryMergeIntoShifterOperand(use, bitfield_op, true);
+ return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true);
}
/**
diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h
index 56804f5e90..83e3ffca57 100644
--- a/compiler/optimizing/instruction_simplifier_shared.h
+++ b/compiler/optimizing/instruction_simplifier_shared.h
@@ -21,6 +21,33 @@
namespace art {
+namespace helpers {
+
+inline bool CanFitInShifterOperand(HInstruction* instruction) {
+ if (instruction->IsTypeConversion()) {
+ HTypeConversion* conversion = instruction->AsTypeConversion();
+ Primitive::Type result_type = conversion->GetResultType();
+ Primitive::Type input_type = conversion->GetInputType();
+ // We don't expect to see the same type as input and result.
+ return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) &&
+ (result_type != input_type);
+ } else {
+ return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) ||
+ (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) ||
+ (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant());
+ }
+}
+
+inline bool HasShifterOperand(HInstruction* instr, InstructionSet isa) {
+ // On ARM64 `neg` instructions are an alias of `sub` using the zero register
+ // as the first register input.
+ bool res = instr->IsAdd() || instr->IsAnd() || (isa == kArm64 && instr->IsNeg()) ||
+ instr->IsOr() || instr->IsSub() || instr->IsXor();
+ return res;
+}
+
+} // namespace helpers
+
bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa);
// For bitwise operations (And/Or/Xor) with a negated input, try to use
// a negated bitwise instruction.
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 95838380cc..26c9ab83c2 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -71,7 +71,7 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,
void HLoopOptimization::Run() {
// Well-behaved loops only.
// TODO: make this less of a sledgehammer.
- if (graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
+ if (!graph_->HasLoops() || graph_->HasTryCatch() || graph_->HasIrreducibleLoops()) {
return;
}
@@ -84,6 +84,10 @@ void HLoopOptimization::Run() {
// Perform loop optimizations.
LocalRun();
+ if (top_loop_ == nullptr) {
+ graph_->SetHasLoops(false);
+ }
+
// Detach.
loop_allocator_ = nullptr;
last_loop_ = top_loop_ = nullptr;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 71a26ebe79..62c89100eb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -688,6 +688,7 @@ void HLoopInformation::Populate() {
contains_irreducible_loop_ = true;
graph->SetHasIrreducibleLoops(true);
}
+ graph->SetHasLoops(true);
}
HBasicBlock* HLoopInformation::GetPreHeader() const {
@@ -2032,9 +2033,19 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
}
}
outer_graph->UpdateMaximumNumberOfOutVRegs(GetMaximumNumberOfOutVRegs());
+
if (HasBoundsChecks()) {
outer_graph->SetHasBoundsChecks(true);
}
+ if (HasLoops()) {
+ outer_graph->SetHasLoops(true);
+ }
+ if (HasIrreducibleLoops()) {
+ outer_graph->SetHasIrreducibleLoops(true);
+ }
+ if (HasTryCatch()) {
+ outer_graph->SetHasTryCatch(true);
+ }
HInstruction* return_value = nullptr;
if (GetBlocks().size() == 3) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 96f9abafbf..8a9e61875a 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -323,6 +323,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
temporaries_vreg_slots_(0),
has_bounds_checks_(false),
has_try_catch_(false),
+ has_loops_(false),
has_irreducible_loops_(false),
debuggable_(debuggable),
current_instruction_id_(start_instruction_id),
@@ -559,6 +560,9 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
bool HasTryCatch() const { return has_try_catch_; }
void SetHasTryCatch(bool value) { has_try_catch_ = value; }
+ bool HasLoops() const { return has_loops_; }
+ void SetHasLoops(bool value) { has_loops_ = value; }
+
bool HasIrreducibleLoops() const { return has_irreducible_loops_; }
void SetHasIrreducibleLoops(bool value) { has_irreducible_loops_ = value; }
@@ -637,14 +641,26 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
// Number of vreg size slots that the temporaries use (used in baseline compiler).
size_t temporaries_vreg_slots_;
- // Has bounds checks. We can totally skip BCE if it's false.
+ // Flag whether there are bounds checks in the graph. We can skip
+ // BCE if it's false. It's only best effort to keep it up to date in
+ // the presence of code elimination so there might be false positives.
bool has_bounds_checks_;
- // Flag whether there are any try/catch blocks in the graph. We will skip
- // try/catch-related passes if false.
+ // Flag whether there are try/catch blocks in the graph. We will skip
+ // try/catch-related passes if it's false. It's only best effort to keep
+ // it up to date in the presence of code elimination so there might be
+ // false positives.
bool has_try_catch_;
- // Flag whether there are any irreducible loops in the graph.
+ // Flag whether there are any loops in the graph. We can skip loop
+ // optimization if it's false. It's only best effort to keep it up
+ // to date in the presence of code elimination so there might be false
+ // positives.
+ bool has_loops_;
+
+ // Flag whether there are any irreducible loops in the graph. It's only
+ // best effort to keep it up to date in the presence of code elimination
+ // so there might be false positives.
bool has_irreducible_loops_;
// Indicates whether the graph should be compiled in a way that
@@ -1346,6 +1362,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
#else
#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \
M(BitwiseNegatedRight, Instruction) \
+ M(DataProcWithShifterOp, Instruction) \
M(MultiplyAccumulate, Instruction) \
M(IntermediateAddress, Instruction)
#endif
@@ -1357,12 +1374,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(ArmDexCacheArraysBase, Instruction)
#endif
-#ifndef ART_ENABLE_CODEGEN_arm64
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)
-#else
-#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
- M(Arm64DataProcWithShifterOp, Instruction)
-#endif
#ifndef ART_ENABLE_CODEGEN_mips
#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M)
@@ -6603,9 +6615,6 @@ class HParallelMove FINAL : public HTemplateInstruction<0> {
#ifdef ART_ENABLE_CODEGEN_arm
#include "nodes_arm.h"
#endif
-#ifdef ART_ENABLE_CODEGEN_arm64
-#include "nodes_arm64.h"
-#endif
#ifdef ART_ENABLE_CODEGEN_mips
#include "nodes_mips.h"
#endif
diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h
deleted file mode 100644
index 3f88717c2a..0000000000
--- a/compiler/optimizing/nodes_arm64.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_OPTIMIZING_NODES_ARM64_H_
-#define ART_COMPILER_OPTIMIZING_NODES_ARM64_H_
-
-#include "nodes.h"
-
-namespace art {
-
-class HArm64DataProcWithShifterOp FINAL : public HExpression<2> {
- public:
- enum OpKind {
- kLSL, // Logical shift left.
- kLSR, // Logical shift right.
- kASR, // Arithmetic shift right.
- kUXTB, // Unsigned extend byte.
- kUXTH, // Unsigned extend half-word.
- kUXTW, // Unsigned extend word.
- kSXTB, // Signed extend byte.
- kSXTH, // Signed extend half-word.
- kSXTW, // Signed extend word.
-
- // Aliases.
- kFirstShiftOp = kLSL,
- kLastShiftOp = kASR,
- kFirstExtensionOp = kUXTB,
- kLastExtensionOp = kSXTW
- };
- HArm64DataProcWithShifterOp(HInstruction* instr,
- HInstruction* left,
- HInstruction* right,
- OpKind op,
- // The shift argument is unused if the operation
- // is an extension.
- int shift = 0,
- uint32_t dex_pc = kNoDexPc)
- : HExpression(instr->GetType(), SideEffects::None(), dex_pc),
- instr_kind_(instr->GetKind()), op_kind_(op), shift_amount_(shift) {
- DCHECK(!instr->HasSideEffects());
- SetRawInputAt(0, left);
- SetRawInputAt(1, right);
- }
-
- bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE {
- const HArm64DataProcWithShifterOp* other = other_instr->AsArm64DataProcWithShifterOp();
- return instr_kind_ == other->instr_kind_ &&
- op_kind_ == other->op_kind_ &&
- shift_amount_ == other->shift_amount_;
- }
-
- static bool IsShiftOp(OpKind op_kind) {
- return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp;
- }
-
- static bool IsExtensionOp(OpKind op_kind) {
- return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp;
- }
-
- // Find the operation kind and shift amount from a bitfield move instruction.
- static void GetOpInfoFromInstruction(HInstruction* bitfield_op,
- /*out*/OpKind* op_kind,
- /*out*/int* shift_amount);
-
- InstructionKind GetInstrKind() const { return instr_kind_; }
- OpKind GetOpKind() const { return op_kind_; }
- int GetShiftAmount() const { return shift_amount_; }
-
- DECLARE_INSTRUCTION(Arm64DataProcWithShifterOp);
-
- private:
- InstructionKind instr_kind_;
- OpKind op_kind_;
- int shift_amount_;
-
- friend std::ostream& operator<<(std::ostream& os, OpKind op);
-
- DISALLOW_COPY_AND_ASSIGN(HArm64DataProcWithShifterOp);
-};
-
-std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op);
-
-} // namespace art
-
-#endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_
diff --git a/compiler/optimizing/nodes_arm64.cc b/compiler/optimizing/nodes_shared.cc
index ac2f093847..f145bf9130 100644
--- a/compiler/optimizing/nodes_arm64.cc
+++ b/compiler/optimizing/nodes_shared.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,15 @@
*/
#include "common_arm64.h"
-#include "nodes.h"
+#include "nodes_shared.h"
namespace art {
-using arm64::helpers::CanFitInShifterOperand;
+using helpers::CanFitInShifterOperand;
-void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction,
- /*out*/OpKind* op_kind,
- /*out*/int* shift_amount) {
+void HDataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction,
+ /*out*/OpKind* op_kind,
+ /*out*/int* shift_amount) {
DCHECK(CanFitInShifterOperand(instruction));
if (instruction->IsShl()) {
*op_kind = kLSL;
@@ -41,12 +41,11 @@ void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruc
int result_size = Primitive::ComponentSize(result_type);
int input_size = Primitive::ComponentSize(input_type);
int min_size = std::min(result_size, input_size);
- // This follows the logic in
- // `InstructionCodeGeneratorARM64::VisitTypeConversion()`.
if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) {
- // There is actually nothing to do. The register will be used as a W
- // register, discarding the top bits. This is represented by the default
- // encoding 'LSL 0'.
+ // There is actually nothing to do. On ARM the high register from the
+ // pair will be ignored. On ARM64 the register will be used as a W
+ // register, discarding the top bits. This is represented by the
+ // default encoding 'LSL 0'.
*op_kind = kLSL;
*shift_amount = 0;
} else if (result_type == Primitive::kPrimChar ||
@@ -64,17 +63,17 @@ void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruc
}
}
-std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op) {
+std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op) {
switch (op) {
- case HArm64DataProcWithShifterOp::kLSL: return os << "LSL";
- case HArm64DataProcWithShifterOp::kLSR: return os << "LSR";
- case HArm64DataProcWithShifterOp::kASR: return os << "ASR";
- case HArm64DataProcWithShifterOp::kUXTB: return os << "UXTB";
- case HArm64DataProcWithShifterOp::kUXTH: return os << "UXTH";
- case HArm64DataProcWithShifterOp::kUXTW: return os << "UXTW";
- case HArm64DataProcWithShifterOp::kSXTB: return os << "SXTB";
- case HArm64DataProcWithShifterOp::kSXTH: return os << "SXTH";
- case HArm64DataProcWithShifterOp::kSXTW: return os << "SXTW";
+ case HDataProcWithShifterOp::kLSL: return os << "LSL";
+ case HDataProcWithShifterOp::kLSR: return os << "LSR";
+ case HDataProcWithShifterOp::kASR: return os << "ASR";
+ case HDataProcWithShifterOp::kUXTB: return os << "UXTB";
+ case HDataProcWithShifterOp::kUXTH: return os << "UXTH";
+ case HDataProcWithShifterOp::kUXTW: return os << "UXTW";
+ case HDataProcWithShifterOp::kSXTB: return os << "SXTB";
+ case HDataProcWithShifterOp::kSXTH: return os << "SXTH";
+ case HDataProcWithShifterOp::kSXTW: return os << "SXTW";
default:
LOG(FATAL) << "Invalid OpKind " << static_cast<int>(op);
UNREACHABLE();
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 814202e97b..c6bfbcc7fb 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -150,6 +150,81 @@ class HIntermediateAddress FINAL : public HExpression<2> {
DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress);
};
+class HDataProcWithShifterOp FINAL : public HExpression<2> {
+ public:
+ enum OpKind {
+ kLSL, // Logical shift left.
+ kLSR, // Logical shift right.
+ kASR, // Arithmetic shift right.
+ kUXTB, // Unsigned extend byte.
+ kUXTH, // Unsigned extend half-word.
+ kUXTW, // Unsigned extend word.
+ kSXTB, // Signed extend byte.
+ kSXTH, // Signed extend half-word.
+ kSXTW, // Signed extend word.
+
+ // Aliases.
+ kFirstShiftOp = kLSL,
+ kLastShiftOp = kASR,
+ kFirstExtensionOp = kUXTB,
+ kLastExtensionOp = kSXTW
+ };
+ HDataProcWithShifterOp(HInstruction* instr,
+ HInstruction* left,
+ HInstruction* right,
+ OpKind op,
+ // The shift argument is unused if the operation
+ // is an extension.
+ int shift = 0,
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(instr->GetType(), SideEffects::None(), dex_pc),
+ instr_kind_(instr->GetKind()), op_kind_(op),
+ shift_amount_(shift & (instr->GetType() == Primitive::kPrimInt
+ ? kMaxIntShiftDistance
+ : kMaxLongShiftDistance)) {
+ DCHECK(!instr->HasSideEffects());
+ SetRawInputAt(0, left);
+ SetRawInputAt(1, right);
+ }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE {
+ const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp();
+ return instr_kind_ == other->instr_kind_ &&
+ op_kind_ == other->op_kind_ &&
+ shift_amount_ == other->shift_amount_;
+ }
+
+ static bool IsShiftOp(OpKind op_kind) {
+ return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp;
+ }
+
+ static bool IsExtensionOp(OpKind op_kind) {
+ return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp;
+ }
+
+ // Find the operation kind and shift amount from a bitfield move instruction.
+ static void GetOpInfoFromInstruction(HInstruction* bitfield_op,
+ /*out*/OpKind* op_kind,
+ /*out*/int* shift_amount);
+
+ InstructionKind GetInstrKind() const { return instr_kind_; }
+ OpKind GetOpKind() const { return op_kind_; }
+ int GetShiftAmount() const { return shift_amount_; }
+
+ DECLARE_INSTRUCTION(DataProcWithShifterOp);
+
+ private:
+ InstructionKind instr_kind_;
+ OpKind op_kind_;
+ int shift_amount_;
+
+ friend std::ostream& operator<<(std::ostream& os, OpKind op);
+
+ DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp);
+};
+
+std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op);
} // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8638e346fb..f72bd6a5a3 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -306,7 +306,7 @@ class OptimizingCompiler FINAL : public Compiler {
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache) const OVERRIDE;
@@ -375,7 +375,7 @@ class OptimizingCompiler FINAL : public Compiler {
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
ArtMethod* method,
@@ -875,7 +875,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject class_loader,
+ Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache,
ArtMethod* method,
@@ -946,11 +946,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
const uint8_t* interpreter_metadata = nullptr;
if (method == nullptr) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> loader(hs.NewHandle(
- soa.Decode<mirror::ClassLoader>(class_loader)));
method = compiler_driver->ResolveMethod(
- soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type);
+ soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type);
}
// For AOT compilation, we may not get a method, for example if its class is erroneous.
// JIT should always have a method.
@@ -959,16 +956,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
graph->SetArtMethod(method);
ScopedObjectAccess soa(Thread::Current());
interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize());
- dex::TypeIndex type_index = method->GetDeclaringClass()->GetDexTypeIndex();
-
- // Update the dex cache if the type is not in it yet. Note that under AOT,
- // the verifier must have set it, but under JIT, there's no guarantee, as we
- // don't necessarily run the verifier.
- // The compiler and the compiler driver assume the compiling class is
- // in the dex cache.
- if (dex_cache->GetResolvedType(type_index) == nullptr) {
- dex_cache->SetResolvedType(type_index, method->GetDeclaringClass());
- }
}
std::unique_ptr<CodeGenerator> codegen(
@@ -1049,7 +1036,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
InvokeType invoke_type,
uint16_t class_def_idx,
uint32_t method_idx,
- jobject jclass_loader,
+ Handle<mirror::ClassLoader> jclass_loader,
const DexFile& dex_file,
Handle<mirror::DexCache> dex_cache) const {
CompilerDriver* compiler_driver = GetCompilerDriver();
@@ -1163,7 +1150,6 @@ bool OptimizingCompiler::JitCompile(Thread* self,
Handle<mirror::DexCache> dex_cache(hs.NewHandle(method->GetDexCache()));
DCHECK(method->IsCompilable());
- jobject jclass_loader = class_loader.ToJObject();
const DexFile* dex_file = method->GetDexFile();
const uint16_t class_def_idx = method->GetClassDefIndex();
const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
@@ -1187,7 +1173,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
invoke_type,
class_def_idx,
method_idx,
- jclass_loader,
+ class_loader,
*dex_file,
dex_cache,
method,
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index c55fccc7d3..6e332ca59b 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -65,11 +65,13 @@ ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetThrowabl
class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
public:
RTPVisitor(HGraph* graph,
+ Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
HandleCache* handle_cache,
ArenaVector<HInstruction*>* worklist,
bool is_first_run)
: HGraphDelegateVisitor(graph),
+ class_loader_(class_loader),
hint_dex_cache_(hint_dex_cache),
handle_cache_(handle_cache),
worklist_(worklist),
@@ -101,6 +103,7 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
bool is_exact);
private:
+ Handle<mirror::ClassLoader> class_loader_;
Handle<mirror::DexCache> hint_dex_cache_;
HandleCache* handle_cache_;
ArenaVector<HInstruction*>* worklist_;
@@ -108,11 +111,13 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
};
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
+ Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
VariableSizedHandleScope* handles,
bool is_first_run,
const char* name)
: HOptimization(graph, name),
+ class_loader_(class_loader),
hint_dex_cache_(hint_dex_cache),
handle_cache_(handles),
worklist_(graph->GetArena()->Adapter(kArenaAllocReferenceTypePropagation)),
@@ -147,7 +152,12 @@ void ReferenceTypePropagation::ValidateTypes() {
}
void ReferenceTypePropagation::Visit(HInstruction* instruction) {
- RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+ RTPVisitor visitor(graph_,
+ class_loader_,
+ hint_dex_cache_,
+ &handle_cache_,
+ &worklist_,
+ is_first_run_);
instruction->Accept(&visitor);
}
@@ -321,7 +331,12 @@ void ReferenceTypePropagation::Run() {
}
void ReferenceTypePropagation::VisitBasicBlock(HBasicBlock* block) {
- RTPVisitor visitor(graph_, hint_dex_cache_, &handle_cache_, &worklist_, is_first_run_);
+ RTPVisitor visitor(graph_,
+ class_loader_,
+ hint_dex_cache_,
+ &handle_cache_,
+ &worklist_,
+ is_first_run_);
// Handle Phis first as there might be instructions in the same block who depend on them.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
VisitPhi(it.Current()->AsPhi());
@@ -542,8 +557,9 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction*
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
- // Get type from dex cache assuming it was populated by the verifier.
- SetClassAsTypeInfo(instr, dex_cache->GetResolvedType(type_idx), is_exact);
+ ObjPtr<mirror::Class> klass =
+ ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get());
+ SetClassAsTypeInfo(instr, klass, is_exact);
}
void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) {
@@ -556,25 +572,13 @@ void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) {
SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
}
-static mirror::Class* GetClassFromDexCache(Thread* self,
- const DexFile& dex_file,
- dex::TypeIndex type_idx,
- Handle<mirror::DexCache> hint_dex_cache)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(self, dex_file, hint_dex_cache);
- // Get type from dex cache assuming it was populated by the verifier.
- return dex_cache->GetResolvedType(type_idx);
-}
-
void ReferenceTypePropagation::RTPVisitor::VisitParameterValue(HParameterValue* instr) {
// We check if the existing type is valid: the inliner may have set it.
if (instr->GetType() == Primitive::kPrimNot && !instr->GetReferenceTypeInfo().IsValid()) {
- ScopedObjectAccess soa(Thread::Current());
- mirror::Class* resolved_class = GetClassFromDexCache(soa.Self(),
- instr->GetDexFile(),
- instr->GetTypeIndex(),
- hint_dex_cache_);
- SetClassAsTypeInfo(instr, resolved_class, /* is_exact */ false);
+ UpdateReferenceTypeInfo(instr,
+ instr->GetTypeIndex(),
+ instr->GetDexFile(),
+ /* is_exact */ false);
}
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 4663471729..215e96786b 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -33,6 +33,7 @@ namespace art {
class ReferenceTypePropagation : public HOptimization {
public:
ReferenceTypePropagation(HGraph* graph,
+ Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
VariableSizedHandleScope* handles,
bool is_first_run,
@@ -105,6 +106,8 @@ class ReferenceTypePropagation : public HOptimization {
void ValidateTypes();
+ Handle<mirror::ClassLoader> class_loader_;
+
// Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with
// graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
// as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index b061c871b0..84a4bab1a9 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -38,6 +38,7 @@ class ReferenceTypePropagationTest : public CommonCompilerTest {
void SetupPropagation(VariableSizedHandleScope* handles) {
graph_->InitializeInexactObjectRTI(handles);
propagation_ = new (&allocator_) ReferenceTypePropagation(graph_,
+ Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
handles,
true,
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index e3701fbcb1..558dcc4cbc 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -31,8 +31,8 @@ void SchedulingLatencyVisitorARM64::VisitBitwiseNegatedRight(
last_visited_latency_ = kArm64IntegerOpLatency;
}
-void SchedulingLatencyVisitorARM64::VisitArm64DataProcWithShifterOp(
- HArm64DataProcWithShifterOp* ATTRIBUTE_UNUSED) {
+void SchedulingLatencyVisitorARM64::VisitDataProcWithShifterOp(
+ HDataProcWithShifterOp* ATTRIBUTE_UNUSED) {
last_visited_latency_ = kArm64DataProcWithShifterOpLatency;
}
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index 702027c535..7a33720655 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -74,7 +74,8 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor {
#define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \
M(BitwiseNegatedRight, unused) \
M(MultiplyAccumulate, unused) \
- M(IntermediateAddress, unused)
+ M(IntermediateAddress, unused) \
+ M(DataProcWithShifterOp, unused)
#define DECLARE_VISIT_INSTRUCTION(type, unused) \
void Visit##type(H##type* instruction) OVERRIDE;
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 487e4dd498..50ab11bc23 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -499,7 +499,11 @@ GraphAnalysisResult SsaBuilder::BuildSsa() {
// 4) Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
- ReferenceTypePropagation(graph_, dex_cache_, handles_, /* is_first_run */ true).Run();
+ ReferenceTypePropagation(graph_,
+ class_loader_,
+ dex_cache_,
+ handles_,
+ /* is_first_run */ true).Run();
// 5) HInstructionBuilder duplicated ArrayGet instructions with ambiguous type
// (int/float or long/double) and marked ArraySets with ambiguous input type.
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 45dac54115..978f113ec4 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -48,9 +48,11 @@ namespace art {
class SsaBuilder : public ValueObject {
public:
SsaBuilder(HGraph* graph,
+ Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> dex_cache,
VariableSizedHandleScope* handles)
: graph_(graph),
+ class_loader_(class_loader),
dex_cache_(dex_cache),
handles_(handles),
agets_fixed_(false),
@@ -115,6 +117,7 @@ class SsaBuilder : public ValueObject {
void RemoveRedundantUninitializedStrings();
HGraph* graph_;
+ Handle<mirror::ClassLoader> class_loader_;
Handle<mirror::DexCache> dex_cache_;
VariableSizedHandleScope* const handles_;
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 5a466e1d5d..6eab302dab 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -642,39 +642,6 @@ void X86Assembler::movhpd(const Address& dst, XmmRegister src) {
}
-void X86Assembler::psrldq(XmmRegister reg, const Immediate& shift_count) {
- DCHECK(shift_count.is_uint8());
-
- AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitUint8(0x66);
- EmitUint8(0x0F);
- EmitUint8(0x73);
- EmitXmmRegisterOperand(3, reg);
- EmitUint8(shift_count.value());
-}
-
-
-void X86Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) {
- DCHECK(shift_count.is_uint8());
-
- AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitUint8(0x66);
- EmitUint8(0x0F);
- EmitUint8(0x73);
- EmitXmmRegisterOperand(2, reg);
- EmitUint8(shift_count.value());
-}
-
-
-void X86Assembler::punpckldq(XmmRegister dst, XmmRegister src) {
- AssemblerBuffer::EnsureCapacity ensured(&buffer_);
- EmitUint8(0x66);
- EmitUint8(0x0F);
- EmitUint8(0x62);
- EmitXmmRegisterOperand(dst, src);
-}
-
-
void X86Assembler::addsd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF2);
@@ -828,6 +795,51 @@ void X86Assembler::movdqu(const Address& dst, XmmRegister src) {
}
+void X86Assembler::paddb(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xFC);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubb(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xF8);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::paddw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xFD);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xF9);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pmullw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xD5);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
void X86Assembler::paddd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -856,6 +868,24 @@ void X86Assembler::pmulld(XmmRegister dst, XmmRegister src) {
}
+void X86Assembler::paddq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xD4);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psubq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0xFB);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF3);
@@ -1186,6 +1216,141 @@ void X86Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm
}
+void X86Assembler::punpcklbw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x60);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::punpcklwd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x61);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::punpckldq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x62);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::punpcklqdq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x6C);
+ EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::psllw(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x71);
+ EmitXmmRegisterOperand(6, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::pslld(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x72);
+ EmitXmmRegisterOperand(6, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psllq(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x73);
+ EmitXmmRegisterOperand(6, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psraw(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x71);
+ EmitXmmRegisterOperand(4, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psrad(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x72);
+ EmitXmmRegisterOperand(4, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psrlw(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x71);
+ EmitXmmRegisterOperand(2, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psrld(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x72);
+ EmitXmmRegisterOperand(2, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x73);
+ EmitXmmRegisterOperand(2, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86Assembler::psrldq(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitUint8(0x0F);
+ EmitUint8(0x73);
+ EmitXmmRegisterOperand(3, reg);
+ EmitUint8(shift_count.value());
+}
+
+
void X86Assembler::fldl(const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xDD);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 4343e2e734..2999599fc5 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -408,14 +408,9 @@ class X86Assembler FINAL : public Assembler {
void movsd(const Address& dst, XmmRegister src);
void movsd(XmmRegister dst, XmmRegister src);
- void psrlq(XmmRegister reg, const Immediate& shift_count);
- void punpckldq(XmmRegister dst, XmmRegister src);
-
void movhpd(XmmRegister dst, const Address& src);
void movhpd(const Address& dst, XmmRegister src);
- void psrldq(XmmRegister reg, const Immediate& shift_count);
-
void addsd(XmmRegister dst, XmmRegister src);
void addsd(XmmRegister dst, const Address& src);
void subsd(XmmRegister dst, XmmRegister src);
@@ -436,10 +431,20 @@ class X86Assembler FINAL : public Assembler {
void movdqa(const Address& dst, XmmRegister src); // store aligned
void movdqu(const Address& dst, XmmRegister src); // store unaligned
- void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void paddb(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void psubb(XmmRegister dst, XmmRegister src);
+
+ void paddw(XmmRegister dst, XmmRegister src);
+ void psubw(XmmRegister dst, XmmRegister src);
+ void pmullw(XmmRegister dst, XmmRegister src);
+
+ void paddd(XmmRegister dst, XmmRegister src);
void psubd(XmmRegister dst, XmmRegister src);
void pmulld(XmmRegister dst, XmmRegister src);
+ void paddq(XmmRegister dst, XmmRegister src);
+ void psubq(XmmRegister dst, XmmRegister src);
+
void cvtsi2ss(XmmRegister dst, Register src);
void cvtsi2sd(XmmRegister dst, Register src);
@@ -489,6 +494,24 @@ class X86Assembler FINAL : public Assembler {
void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm);
void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm);
+ void punpcklbw(XmmRegister dst, XmmRegister src);
+ void punpcklwd(XmmRegister dst, XmmRegister src);
+ void punpckldq(XmmRegister dst, XmmRegister src);
+ void punpcklqdq(XmmRegister dst, XmmRegister src);
+
+ void psllw(XmmRegister reg, const Immediate& shift_count);
+ void pslld(XmmRegister reg, const Immediate& shift_count);
+ void psllq(XmmRegister reg, const Immediate& shift_count);
+
+ void psraw(XmmRegister reg, const Immediate& shift_count);
+ void psrad(XmmRegister reg, const Immediate& shift_count);
+ // no psraq
+
+ void psrlw(XmmRegister reg, const Immediate& shift_count);
+ void psrld(XmmRegister reg, const Immediate& shift_count);
+ void psrlq(XmmRegister reg, const Immediate& shift_count);
+ void psrldq(XmmRegister reg, const Immediate& shift_count);
+
void flds(const Address& src);
void fstps(const Address& dst);
void fsts(const Address& dst);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index c6ab893aea..a74bea207e 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -122,18 +122,6 @@ TEST_F(AssemblerX86Test, Movntl) {
DriverStr(expected, "movntl");
}
-TEST_F(AssemblerX86Test, psrlq) {
- GetAssembler()->psrlq(x86::XMM0, CreateImmediate(32));
- const char* expected = "psrlq $0x20, %xmm0\n";
- DriverStr(expected, "psrlq");
-}
-
-TEST_F(AssemblerX86Test, punpckldq) {
- GetAssembler()->punpckldq(x86::XMM0, x86::XMM1);
- const char* expected = "punpckldq %xmm1, %xmm0\n";
- DriverStr(expected, "punpckldq");
-}
-
TEST_F(AssemblerX86Test, LoadLongConstant) {
GetAssembler()->LoadLongConstant(x86::XMM0, 51);
const char* expected =
@@ -521,6 +509,26 @@ TEST_F(AssemblerX86Test, DivPD) {
DriverStr(RepeatFF(&x86::X86Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd");
}
+TEST_F(AssemblerX86Test, PAddB) {
+ DriverStr(RepeatFF(&x86::X86Assembler::paddb, "paddb %{reg2}, %{reg1}"), "paddb");
+}
+
+TEST_F(AssemblerX86Test, PSubB) {
+ DriverStr(RepeatFF(&x86::X86Assembler::psubb, "psubb %{reg2}, %{reg1}"), "psubb");
+}
+
+TEST_F(AssemblerX86Test, PAddW) {
+ DriverStr(RepeatFF(&x86::X86Assembler::paddw, "paddw %{reg2}, %{reg1}"), "paddw");
+}
+
+TEST_F(AssemblerX86Test, PSubW) {
+ DriverStr(RepeatFF(&x86::X86Assembler::psubw, "psubw %{reg2}, %{reg1}"), "psubw");
+}
+
+TEST_F(AssemblerX86Test, PMullW) {
+ DriverStr(RepeatFF(&x86::X86Assembler::pmullw, "pmullw %{reg2}, %{reg1}"), "pmullw");
+}
+
TEST_F(AssemblerX86Test, PAddD) {
DriverStr(RepeatFF(&x86::X86Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd");
}
@@ -533,6 +541,14 @@ TEST_F(AssemblerX86Test, PMullD) {
DriverStr(RepeatFF(&x86::X86Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld");
}
+TEST_F(AssemblerX86Test, PAddQ) {
+ DriverStr(RepeatFF(&x86::X86Assembler::paddq, "paddq %{reg2}, %{reg1}"), "paddq");
+}
+
+TEST_F(AssemblerX86Test, PSubQ) {
+ DriverStr(RepeatFF(&x86::X86Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq");
+}
+
TEST_F(AssemblerX86Test, XorPD) {
DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd");
}
@@ -581,6 +597,67 @@ TEST_F(AssemblerX86Test, PShufD) {
DriverStr(RepeatFFI(&x86::X86Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd");
}
+TEST_F(AssemblerX86Test, Punpcklbw) {
+ DriverStr(RepeatFF(&x86::X86Assembler::punpcklbw, "punpcklbw %{reg2}, %{reg1}"), "punpcklbw");
+}
+
+TEST_F(AssemblerX86Test, Punpcklwd) {
+ DriverStr(RepeatFF(&x86::X86Assembler::punpcklwd, "punpcklwd %{reg2}, %{reg1}"), "punpcklwd");
+}
+
+TEST_F(AssemblerX86Test, Punpckldq) {
+ DriverStr(RepeatFF(&x86::X86Assembler::punpckldq, "punpckldq %{reg2}, %{reg1}"), "punpckldq");
+}
+
+TEST_F(AssemblerX86Test, Punpcklqdq) {
+ DriverStr(RepeatFF(&x86::X86Assembler::punpcklqdq, "punpcklqdq %{reg2}, %{reg1}"), "punpcklqdq");
+}
+
+TEST_F(AssemblerX86Test, psllw) {
+ GetAssembler()->psllw(x86::XMM0, CreateImmediate(16));
+ DriverStr("psllw $0x10, %xmm0\n", "psllwi");
+}
+
+TEST_F(AssemblerX86Test, pslld) {
+ GetAssembler()->pslld(x86::XMM0, CreateImmediate(16));
+ DriverStr("pslld $0x10, %xmm0\n", "pslldi");
+}
+
+TEST_F(AssemblerX86Test, psllq) {
+ GetAssembler()->psllq(x86::XMM0, CreateImmediate(16));
+ DriverStr("psllq $0x10, %xmm0\n", "psllqi");
+}
+
+TEST_F(AssemblerX86Test, psraw) {
+ GetAssembler()->psraw(x86::XMM0, CreateImmediate(16));
+ DriverStr("psraw $0x10, %xmm0\n", "psrawi");
+}
+
+TEST_F(AssemblerX86Test, psrad) {
+ GetAssembler()->psrad(x86::XMM0, CreateImmediate(16));
+ DriverStr("psrad $0x10, %xmm0\n", "psradi");
+}
+
+TEST_F(AssemblerX86Test, psrlw) {
+ GetAssembler()->psrlw(x86::XMM0, CreateImmediate(16));
+ DriverStr("psrlw $0x10, %xmm0\n", "psrlwi");
+}
+
+TEST_F(AssemblerX86Test, psrld) {
+ GetAssembler()->psrld(x86::XMM0, CreateImmediate(16));
+ DriverStr("psrld $0x10, %xmm0\n", "psrldi");
+}
+
+TEST_F(AssemblerX86Test, psrlq) {
+ GetAssembler()->psrlq(x86::XMM0, CreateImmediate(16));
+ DriverStr("psrlq $0x10, %xmm0\n", "psrlqi");
+}
+
+TEST_F(AssemblerX86Test, psrldq) {
+ GetAssembler()->psrldq(x86::XMM0, CreateImmediate(16));
+ DriverStr("psrldq $0x10, %xmm0\n", "psrldqi");
+}
+
/////////////////
// Near labels //
/////////////////
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index b41be80ae4..458204aca9 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -882,6 +882,56 @@ void X86_64Assembler::movdqu(const Address& dst, XmmRegister src) {
}
+void X86_64Assembler::paddb(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xFC);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubb(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xF8);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::paddw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xFD);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xF9);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::pmullw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xD5);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
void X86_64Assembler::paddd(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -913,6 +963,26 @@ void X86_64Assembler::pmulld(XmmRegister dst, XmmRegister src) {
}
+void X86_64Assembler::paddq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xD4);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psubq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0xFB);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) {
cvtsi2ss(dst, src, false);
}
@@ -1354,6 +1424,142 @@ void X86_64Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate&
}
+void X86_64Assembler::punpcklbw(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x60);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::punpcklwd(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x61);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::punpckldq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x62);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::punpcklqdq(XmmRegister dst, XmmRegister src) {
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex32(dst, src);
+ EmitUint8(0x0F);
+ EmitUint8(0x6C);
+ EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+
+void X86_64Assembler::psllw(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x71);
+ EmitXmmRegisterOperand(6, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::pslld(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x72);
+ EmitXmmRegisterOperand(6, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::psllq(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x73);
+ EmitXmmRegisterOperand(6, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::psraw(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x71);
+ EmitXmmRegisterOperand(4, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::psrad(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x72);
+ EmitXmmRegisterOperand(4, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::psrlw(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x71);
+ EmitXmmRegisterOperand(2, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::psrld(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x72);
+ EmitXmmRegisterOperand(2, reg);
+ EmitUint8(shift_count.value());
+}
+
+
+void X86_64Assembler::psrlq(XmmRegister reg, const Immediate& shift_count) {
+ DCHECK(shift_count.is_uint8());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ EmitUint8(0x66);
+ EmitOptionalRex(false, false, false, false, reg.NeedsRex());
+ EmitUint8(0x0F);
+ EmitUint8(0x73);
+ EmitXmmRegisterOperand(2, reg);
+ EmitUint8(shift_count.value());
+}
+
+
void X86_64Assembler::fldl(const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xDD);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 43ea12a4cb..0dc11d840b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -452,10 +452,20 @@ class X86_64Assembler FINAL : public Assembler {
void movdqa(const Address& dst, XmmRegister src); // store aligned
void movdqu(const Address& dst, XmmRegister src); // store unaligned
- void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void paddb(XmmRegister dst, XmmRegister src); // no addr variant (for now)
+ void psubb(XmmRegister dst, XmmRegister src);
+
+ void paddw(XmmRegister dst, XmmRegister src);
+ void psubw(XmmRegister dst, XmmRegister src);
+ void pmullw(XmmRegister dst, XmmRegister src);
+
+ void paddd(XmmRegister dst, XmmRegister src);
void psubd(XmmRegister dst, XmmRegister src);
void pmulld(XmmRegister dst, XmmRegister src);
+ void paddq(XmmRegister dst, XmmRegister src);
+ void psubq(XmmRegister dst, XmmRegister src);
+
void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version.
void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit);
void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit);
@@ -512,6 +522,23 @@ class X86_64Assembler FINAL : public Assembler {
void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm);
void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm);
+ void punpcklbw(XmmRegister dst, XmmRegister src);
+ void punpcklwd(XmmRegister dst, XmmRegister src);
+ void punpckldq(XmmRegister dst, XmmRegister src);
+ void punpcklqdq(XmmRegister dst, XmmRegister src);
+
+ void psllw(XmmRegister reg, const Immediate& shift_count);
+ void pslld(XmmRegister reg, const Immediate& shift_count);
+ void psllq(XmmRegister reg, const Immediate& shift_count);
+
+ void psraw(XmmRegister reg, const Immediate& shift_count);
+ void psrad(XmmRegister reg, const Immediate& shift_count);
+ // no psraq
+
+ void psrlw(XmmRegister reg, const Immediate& shift_count);
+ void psrld(XmmRegister reg, const Immediate& shift_count);
+ void psrlq(XmmRegister reg, const Immediate& shift_count);
+
void flds(const Address& src);
void fstps(const Address& dst);
void fsts(const Address& dst);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index aeb1911835..fe9449720f 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1128,6 +1128,26 @@ TEST_F(AssemblerX86_64Test, Divpd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd");
}
+TEST_F(AssemblerX86_64Test, Paddb) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddb, "paddb %{reg2}, %{reg1}"), "paddb");
+}
+
+TEST_F(AssemblerX86_64Test, Psubb) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubb, "psubb %{reg2}, %{reg1}"), "psubb");
+}
+
+TEST_F(AssemblerX86_64Test, Paddw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddw, "paddw %{reg2}, %{reg1}"), "paddw");
+}
+
+TEST_F(AssemblerX86_64Test, Psubw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubw, "psubw %{reg2}, %{reg1}"), "psubw");
+}
+
+TEST_F(AssemblerX86_64Test, Pmullw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmullw, "pmullw %{reg2}, %{reg1}"), "pmullw");
+}
+
TEST_F(AssemblerX86_64Test, Paddd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd");
}
@@ -1140,6 +1160,14 @@ TEST_F(AssemblerX86_64Test, Pmulld) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld");
}
+TEST_F(AssemblerX86_64Test, Paddq) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddq, "paddq %{reg2}, %{reg1}"), "paddq");
+}
+
+TEST_F(AssemblerX86_64Test, Psubq) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubq, "psubq %{reg2}, %{reg1}"), "psubq");
+}
+
TEST_F(AssemblerX86_64Test, Cvtsi2ss) {
DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss");
}
@@ -1261,6 +1289,78 @@ TEST_F(AssemblerX86_64Test, PShufd) {
DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd");
}
+TEST_F(AssemblerX86_64Test, Punpcklbw) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklbw, "punpcklbw %{reg2}, %{reg1}"), "punpcklbw");
+}
+
+TEST_F(AssemblerX86_64Test, Punpcklwd) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklwd, "punpcklwd %{reg2}, %{reg1}"), "punpcklwd");
+}
+
+TEST_F(AssemblerX86_64Test, Punpckldq) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpckldq, "punpckldq %{reg2}, %{reg1}"), "punpckldq");
+}
+
+TEST_F(AssemblerX86_64Test, Punpcklqdq) {
+ DriverStr(RepeatFF(&x86_64::X86_64Assembler::punpcklqdq, "punpcklqdq %{reg2}, %{reg1}"), "punpcklqdq");
+}
+
+TEST_F(AssemblerX86_64Test, Psllw) {
+ GetAssembler()->psllw(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psllw(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psllw $1, %xmm0\n"
+ "psllw $2, %xmm15\n", "psllwi");
+}
+
+TEST_F(AssemblerX86_64Test, Pslld) {
+ GetAssembler()->pslld(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->pslld(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("pslld $1, %xmm0\n"
+ "pslld $2, %xmm15\n", "pslldi");
+}
+
+TEST_F(AssemblerX86_64Test, Psllq) {
+ GetAssembler()->psllq(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psllq(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psllq $1, %xmm0\n"
+ "psllq $2, %xmm15\n", "psllqi");
+}
+
+TEST_F(AssemblerX86_64Test, Psraw) {
+ GetAssembler()->psraw(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psraw(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psraw $1, %xmm0\n"
+ "psraw $2, %xmm15\n", "psrawi");
+}
+
+TEST_F(AssemblerX86_64Test, Psrad) {
+ GetAssembler()->psrad(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psrad(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psrad $1, %xmm0\n"
+ "psrad $2, %xmm15\n", "psradi");
+}
+
+TEST_F(AssemblerX86_64Test, Psrlw) {
+ GetAssembler()->psrlw(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psrlw(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psrlw $1, %xmm0\n"
+ "psrlw $2, %xmm15\n", "psrlwi");
+}
+
+TEST_F(AssemblerX86_64Test, Psrld) {
+ GetAssembler()->psrld(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psrld(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psrld $1, %xmm0\n"
+ "psrld $2, %xmm15\n", "pslldi");
+}
+
+TEST_F(AssemblerX86_64Test, Psrlq) {
+ GetAssembler()->psrlq(x86_64::XmmRegister(x86_64::XMM0), x86_64::Immediate(1));
+ GetAssembler()->psrlq(x86_64::XmmRegister(x86_64::XMM15), x86_64::Immediate(2));
+ DriverStr("psrlq $1, %xmm0\n"
+ "psrlq $2, %xmm15\n", "pslrqi");
+}
+
TEST_F(AssemblerX86_64Test, UcomissAddress) {
GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(
x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12));
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 026a567fe0..e6b793011f 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2095,7 +2095,7 @@ class Dex2Oat FINAL {
}
bool UseProfileGuidedCompilation() const {
- return CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter());
+ return profile_file_fd_ != -1 || !profile_file_.empty();
}
bool LoadProfile() {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 5dcdd9e69a..6881f75c75 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -400,9 +400,9 @@ class Dex2oatSwapUseTest : public Dex2oatSwapTest {
TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) {
// The `native_alloc_2_ >= native_alloc_1_` assertion below may not
- // hold true on some x86 systems when read barriers are enabled;
- // disable this test while we investigate (b/29259363).
- TEST_DISABLED_FOR_READ_BARRIER_ON_X86();
+ // hold true on some x86 systems; disable this test while we
+ // investigate (b/29259363).
+ TEST_DISABLED_FOR_X86();
RunTest(false /* use_fd */,
false /* expect_use */);
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 43de34241b..2d9bbfdbb7 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -466,8 +466,8 @@ AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem*
AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file,
- const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset) {
- if (disk_annotations_item.size_ == 0 && offset == 0) {
+ const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset) {
+ if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) {
return nullptr;
}
auto found_anno_set_item = AnnotationSetItems().find(offset);
@@ -475,14 +475,14 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file,
return found_anno_set_item->second.get();
}
std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>();
- for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
+ for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) {
const DexFile::AnnotationItem* annotation =
- dex_file.GetAnnotationItem(&disk_annotations_item, i);
+ dex_file.GetAnnotationItem(disk_annotations_item, i);
if (annotation == nullptr) {
continue;
}
AnnotationItem* annotation_item =
- CreateAnnotationItem(annotation, disk_annotations_item.entries_[i]);
+ CreateAnnotationItem(annotation, disk_annotations_item->entries_[i]);
items->push_back(annotation_item);
}
AnnotationSetItem* annotation_set_item = new AnnotationSetItem(items);
@@ -501,7 +501,7 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF
AnnotationSetItem* class_annotation = nullptr;
if (class_set_item != nullptr) {
uint32_t offset = disk_annotations_item->class_annotations_off_;
- class_annotation = CreateAnnotationSetItem(dex_file, *class_set_item, offset);
+ class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, offset);
}
const DexFile::FieldAnnotationsItem* fields =
dex_file.GetFieldAnnotations(disk_annotations_item);
@@ -514,7 +514,7 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF
dex_file.GetFieldAnnotationSetItem(fields[i]);
uint32_t annotation_set_offset = fields[i].annotations_off_;
AnnotationSetItem* annotation_set_item =
- CreateAnnotationSetItem(dex_file, *field_set_item, annotation_set_offset);
+ CreateAnnotationSetItem(dex_file, field_set_item, annotation_set_offset);
field_annotations->push_back(std::unique_ptr<FieldAnnotation>(
new FieldAnnotation(field_id, annotation_set_item)));
}
@@ -530,7 +530,7 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF
dex_file.GetMethodAnnotationSetItem(methods[i]);
uint32_t annotation_set_offset = methods[i].annotations_off_;
AnnotationSetItem* annotation_set_item =
- CreateAnnotationSetItem(dex_file, *method_set_item, annotation_set_offset);
+ CreateAnnotationSetItem(dex_file, method_set_item, annotation_set_offset);
method_annotations->push_back(std::unique_ptr<MethodAnnotation>(
new MethodAnnotation(method_id, annotation_set_item)));
}
@@ -569,7 +569,7 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation(
const DexFile::AnnotationSetItem* annotation_set_item =
dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
- annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset));
+ annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset));
}
set_ref_list = new AnnotationSetRefList(annotations);
annotation_set_ref_lists_.AddItem(set_ref_list, offset);
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 3a5b64480f..96afb906c7 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -194,7 +194,7 @@ class Collections {
EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset);
AnnotationItem* CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset);
AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file,
- const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset);
+ const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset);
AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file,
const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset);
CodeItem* CreateCodeItem(
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index da1e1d26dc..2d85e8fe7b 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -41,7 +41,7 @@ static const char kDexFileLayoutInputDex[] =
"AAAAdQEAAAAQAAABAAAAjAEAAA==";
static const char kDexFileLayoutInputProfile[] =
- "cHJvADAwMgABAAsAAAABAPUpbf5jbGFzc2VzLmRleAEA";
+ "cHJvADAwMwABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA==";
static const char kDexFileLayoutExpectedOutputDex[] =
"ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH"
diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc
index ff05733345..a289433af5 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -1306,7 +1306,7 @@ DISASSEMBLER_ENTRY(cmp,
has_modrm = true;
reg_is_opcode = true;
store = true;
- immediate_bytes = ((instr[1] & 0x38) == 0) ? 1 : 0;
+ immediate_bytes = ((instr[1] & 0x38) == 0) ? (instr[0] == 0xF7 ? 4 : 1) : 0;
break;
case 0xFF:
{
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index d80df70738..aa07d2491a 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -41,7 +41,7 @@ endif
.PHONY: dump-oat-core-target-$(TARGET_ARCH)
ifeq ($(ART_BUILD_TARGET),true)
-dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_default_no-pic_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP)
+dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_default_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP)
$(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \
--output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH)
@echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt
@@ -50,7 +50,7 @@ endif
ifdef TARGET_2ND_ARCH
.PHONY: dump-oat-core-target-$(TARGET_2ND_ARCH)
ifeq ($(ART_BUILD_TARGET),true)
-dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_default_no-pic_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP)
+dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_default_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP)
$(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \
--output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH)
@echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 44132868a1..becb827d20 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1018,7 +1018,9 @@ class OatDumper {
dex_orig_name = dex_file_location.substr(dex_orig_pos + 1);
// A more elegant approach to efficiently name user installed apps is welcome
- if (dex_orig_name.size() == 8 && !dex_orig_name.compare("base.apk")) {
+ if (dex_orig_name.size() == 8 &&
+ dex_orig_name.compare("base.apk") == 0 &&
+ dex_orig_pos != std::string::npos) {
dex_file_location.erase(dex_orig_pos, strlen("base.apk") + 1);
size_t apk_orig_pos = dex_file_location.rfind('/');
if (apk_orig_pos != std::string::npos) {
@@ -2242,9 +2244,14 @@ class ImageDumper {
ScopedIndentation indent2(&state->vios_);
auto* resolved_types = dex_cache->GetResolvedTypes();
for (size_t i = 0; i < num_types; ++i) {
- auto* elem = resolved_types[i].Read();
+ auto pair = resolved_types[i].load(std::memory_order_relaxed);
size_t run = 0;
- for (size_t j = i + 1; j != num_types && elem == resolved_types[j].Read(); ++j) {
+ for (size_t j = i + 1; j != num_types; ++j) {
+ auto other_pair = resolved_types[j].load(std::memory_order_relaxed);
+ if (pair.index != other_pair.index ||
+ pair.object.Read() != other_pair.object.Read()) {
+ break;
+ }
++run;
}
if (run == 0) {
@@ -2254,12 +2261,13 @@ class ImageDumper {
i = i + run;
}
std::string msg;
+ auto* elem = pair.object.Read();
if (elem == nullptr) {
msg = "null";
} else {
msg = elem->PrettyClass();
}
- os << StringPrintf("%p %s\n", elem, msg.c_str());
+ os << StringPrintf("%p %u %s\n", elem, pair.index, msg.c_str());
}
}
}
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index b9be5f2605..491e73980f 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -643,8 +643,8 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots
if (orig_strings != nullptr) {
orig_dex_cache->FixupStrings(RelocatedCopyOf(orig_strings), RelocatedPointerVisitor(this));
}
- GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
- GcRoot<mirror::Class>* relocated_types = RelocatedAddressOfPointer(orig_types);
+ mirror::TypeDexCacheType* orig_types = orig_dex_cache->GetResolvedTypes();
+ mirror::TypeDexCacheType* relocated_types = RelocatedAddressOfPointer(orig_types);
copy_dex_cache->SetField64<false>(
mirror::DexCache::ResolvedTypesOffset(),
static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_types)));
diff --git a/profman/profman.cc b/profman/profman.cc
index 8f35a76b6d..a42e4f1db1 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -593,7 +593,7 @@ class ProfMan FINAL {
}
// Generate the profile data structure.
ProfileCompilationInfo info;
- std::vector<MethodReference> methods; // No methods for now.
+ std::vector<ProfileMethodInfo> methods; // No methods for now.
info.AddMethodsAndClasses(methods, resolved_class_set);
// Write the profile file.
CHECK(info.Save(fd));
diff --git a/runtime/Android.bp b/runtime/Android.bp
index d3a81a9add..d136aa15b3 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -143,7 +143,6 @@ cc_defaults {
"native_bridge_art_interface.cc",
"native_stack_dump.cc",
"native/dalvik_system_DexFile.cc",
- "native/dalvik_system_InMemoryDexClassLoader_DexData.cc",
"native/dalvik_system_VMDebug.cc",
"native/dalvik_system_VMRuntime.cc",
"native/dalvik_system_VMStack.cc",
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 80af8e7bde..16b73c681f 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -311,6 +311,8 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) {
template <bool kResolve>
inline ObjPtr<mirror::Class> ArtField::GetType() {
+ // TODO: Refactor this function into two functions, ResolveType() and LookupType()
+ // so that we can properly annotate it with no-suspension possible / suspension possible.
const uint32_t field_index = GetDexFieldIndex();
ObjPtr<mirror::Class> declaring_class = GetDeclaringClass();
if (UNLIKELY(declaring_class->IsProxyClass())) {
@@ -320,9 +322,16 @@ inline ObjPtr<mirror::Class> ArtField::GetType() {
const DexFile* const dex_file = dex_cache->GetDexFile();
const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index);
ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(field_id.type_idx_);
- if (kResolve && UNLIKELY(type == nullptr)) {
- type = ResolveGetType(field_id.type_idx_);
- CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+ if (UNLIKELY(type == nullptr)) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (kResolve) {
+ type = class_linker->ResolveType(*dex_file, field_id.type_idx_, declaring_class);
+ CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+ } else {
+ type = class_linker->LookupResolvedType(
+ *dex_file, field_id.type_idx_, dex_cache, declaring_class->GetClassLoader());
+ DCHECK(!Thread::Current()->IsExceptionPending());
+ }
}
return type;
}
diff --git a/runtime/art_field.cc b/runtime/art_field.cc
index a4a6e5a4fb..7e131040be 100644
--- a/runtime/art_field.cc
+++ b/runtime/art_field.cc
@@ -48,10 +48,6 @@ ObjPtr<mirror::Class> ArtField::ProxyFindSystemClass(const char* descriptor) {
return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor);
}
-ObjPtr<mirror::Class> ArtField::ResolveGetType(dex::TypeIndex type_idx) {
- return Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
-}
-
ObjPtr<mirror::String> ArtField::ResolveGetStringName(Thread* self,
const DexFile& dex_file,
dex::StringIndex string_idx,
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 427e103749..75dd981136 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -217,8 +217,6 @@ class ArtField FINAL {
private:
ObjPtr<mirror::Class> ProxyFindSystemClass(const char* descriptor)
REQUIRES_SHARED(Locks::mutator_lock_);
- ObjPtr<mirror::Class> ResolveGetType(dex::TypeIndex type_idx)
- REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<mirror::String> ResolveGetStringName(Thread* self,
const DexFile& dex_file,
dex::StringIndex string_idx,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 950f1aa9f4..473d9cf74e 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -175,12 +175,19 @@ inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerS
}
inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
+ // TODO: Refactor this function into two functions, Resolve...() and Lookup...()
+ // so that we can properly annotate it with no-suspension possible / suspension possible.
ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
- if (UNLIKELY(type == nullptr) && resolve) {
+ if (UNLIKELY(type == nullptr)) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- type = class_linker->ResolveType(type_idx, this);
- CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+ if (resolve) {
+ type = class_linker->ResolveType(type_idx, this);
+ CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
+ } else {
+ type = class_linker->LookupResolvedType(
+ *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader());
+ }
}
return type.Ptr();
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 3438810069..bd510ca0e1 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -78,6 +78,18 @@ inline mirror::String* ClassLinker::ResolveString(dex::StringIndex string_idx,
return string.Ptr();
}
+inline ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(
+ dex::TypeIndex type_idx,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
+ ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+ if (type == nullptr) {
+ type = Runtime::Current()->GetClassLinker()->LookupResolvedType(
+ *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader);
+ }
+ return type;
+}
+
inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
Thread::PoisonObjectPointersIfDebug();
if (kIsDebugBuild) {
@@ -91,25 +103,6 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
const DexFile& dex_file = *dex_cache->GetDexFile();
resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
- // Note: We cannot check here to see whether we added the type to the cache. The type
- // might be an erroneous class, which results in it being hidden from us.
- }
- return resolved_type.Ptr();
-}
-
-inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtField* referrer) {
- Thread::PoisonObjectPointersIfDebug();
- ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
- ObjPtr<mirror::DexCache> dex_cache_ptr = declaring_class->GetDexCache();
- ObjPtr<mirror::Class> resolved_type = dex_cache_ptr->GetResolvedType(type_idx);
- if (UNLIKELY(resolved_type == nullptr)) {
- StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(dex_cache_ptr));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- const DexFile& dex_file = *dex_cache->GetDexFile();
- resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
- // Note: We cannot check here to see whether we added the type to the cache. The type
- // might be an erroneous class, which results in it being hidden from us.
}
return resolved_type.Ptr();
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d02cf17d44..46f16449ed 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -58,6 +58,7 @@
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
+#include "gc/space/space-inl.h"
#include "handle_scope-inl.h"
#include "image-inl.h"
#include "imt_conflict_table.h"
@@ -1156,6 +1157,35 @@ class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
ClassTable* const table_;
};
+class VerifyDirectInterfacesInTableClassVisitor {
+ public:
+ explicit VerifyDirectInterfacesInTableClassVisitor(ObjPtr<mirror::ClassLoader> class_loader)
+ : class_loader_(class_loader), self_(Thread::Current()) { }
+
+ bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsPrimitive() && klass->GetClassLoader() == class_loader_) {
+ classes_.push_back(klass);
+ }
+ return true;
+ }
+
+ void Check() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (ObjPtr<mirror::Class> klass : classes_) {
+ for (uint32_t i = 0, num = klass->NumDirectInterfaces(); i != num; ++i) {
+ CHECK(klass->GetDirectInterface(self_, klass, i) != nullptr)
+ << klass->PrettyDescriptor() << " iface #" << i
+ << klass->GetDexFile().StringByTypeIdx(klass->GetDirectInterfaceTypeIdx(i))
+ << " Bug: 34839984";
+ }
+ }
+ }
+
+ private:
+ ObjPtr<mirror::ClassLoader> class_loader_;
+ Thread* self_;
+ std::vector<ObjPtr<mirror::Class>> classes_;
+};
+
class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
public:
VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -1187,6 +1217,23 @@ static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred
}
}
+template <typename T>
+static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src,
+ size_t count,
+ std::atomic<mirror::DexCachePair<T>>* dst) {
+ DCHECK_NE(count, 0u);
+ DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() ||
+ src[0].load(std::memory_order_relaxed).index != 0u);
+ for (size_t i = 0; i < count; ++i) {
+ DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u);
+ DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull());
+ mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed);
+ if (source.index != 0u || !source.object.IsNull()) {
+ dst[i].store(source, std::memory_order_relaxed);
+ }
+ }
+}
+
bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -1240,7 +1287,10 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
if (dex_file->NumStringIds() < num_strings) {
num_strings = dex_file->NumStringIds();
}
- const size_t num_types = dex_file->NumTypeIds();
+ size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
+ if (dex_file->NumTypeIds() < num_types) {
+ num_types = dex_file->NumTypeIds();
+ }
const size_t num_methods = dex_file->NumMethodIds();
const size_t num_fields = dex_file->NumFieldIds();
size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
@@ -1260,28 +1310,14 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings();
mirror::StringDexCacheType* const strings =
reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
- for (size_t j = 0; j < num_strings; ++j) {
- DCHECK_EQ(strings[j].load(std::memory_order_relaxed).index, 0u);
- DCHECK(strings[j].load(std::memory_order_relaxed).object.IsNull());
- strings[j].store(image_resolved_strings[j].load(std::memory_order_relaxed),
- std::memory_order_relaxed);
- }
- mirror::StringDexCachePair::Initialize(strings);
+ CopyDexCachePairs(image_resolved_strings, num_strings, strings);
dex_cache->SetStrings(strings);
}
if (num_types != 0u) {
- GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
- GcRoot<mirror::Class>* const types =
- reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
- for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
- DCHECK(types[j].IsNull());
- }
- CopyNonNull(image_resolved_types,
- num_types,
- types,
- [](const GcRoot<mirror::Class>& elem) {
- return elem.IsNull();
- });
+ mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes();
+ mirror::TypeDexCacheType* const types =
+ reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
+ CopyDexCachePairs(image_resolved_types, num_types, types);
dex_cache->SetResolvedTypes(types);
}
if (num_methods != 0u) {
@@ -1322,15 +1358,7 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
mirror::MethodTypeDexCacheType* const method_types =
reinterpret_cast<mirror::MethodTypeDexCacheType*>(
raw_arrays + layout.MethodTypesOffset());
- for (size_t j = 0; j < num_method_types; ++j) {
- DCHECK_EQ(method_types[j].load(std::memory_order_relaxed).index, 0u);
- DCHECK(method_types[j].load(std::memory_order_relaxed).object.IsNull());
- method_types[j].store(
- image_resolved_method_types[j].load(std::memory_order_relaxed),
- std::memory_order_relaxed);
- }
-
- mirror::MethodTypeDexCachePair::Initialize(method_types);
+ CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types);
dex_cache->SetResolvedMethodTypes(method_types);
}
if (num_call_sites != 0u) {
@@ -1360,11 +1388,11 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
}
if (kIsDebugBuild) {
CHECK(new_class_set != nullptr);
- GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
+ mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
const size_t num_types = dex_cache->NumResolvedTypes();
- for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
+ for (size_t j = 0; j != num_types; ++j) {
// The image space is not yet added to the heap, avoid read barriers.
- ObjPtr<mirror::Class> klass = types[j].Read();
+ ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
if (space->HasAddress(klass.Ptr())) {
DCHECK(!klass->IsErroneous()) << klass->GetStatus();
auto it = new_class_set->Find(ClassTable::TableSlot(klass));
@@ -1723,9 +1751,9 @@ bool ClassLinker::AddImageSpace(
// The current dex file field is bogus, overwrite it so that we can get the dex file in the
// loop below.
dex_cache->SetDexFile(dex_file.get());
- GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
+ mirror::TypeDexCacheType* const types = dex_cache->GetResolvedTypes();
for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
- ObjPtr<mirror::Class> klass = types[j].Read();
+ ObjPtr<mirror::Class> klass = types[j].load(std::memory_order_relaxed).object.Read();
if (klass != nullptr) {
DCHECK(!klass->IsErroneous()) << klass->GetStatus();
}
@@ -1893,6 +1921,12 @@ bool ClassLinker::AddImageSpace(
VerifyClassInTableArtMethodVisitor visitor2(class_table);
header.VisitPackedArtMethods(&visitor2, space->Begin(), kRuntimePointerSize);
}
+ if (app_image) {
+ // TODO: Restrict this check to debug builds. Bug: 34839984
+ VerifyDirectInterfacesInTableClassVisitor visitor(class_loader.Get());
+ class_table->Visit(visitor);
+ visitor.Check();
+ }
VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
return true;
}
@@ -1908,6 +1942,13 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
const bool tracing_enabled = Trace::IsTracingEnabled();
Thread* const self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
+ if (kUseReadBarrier) {
+ // We do not track new roots for CC.
+ DCHECK_EQ(0, flags & (kVisitRootFlagNewRoots |
+ kVisitRootFlagClearRootLog |
+ kVisitRootFlagStartLoggingNewRoots |
+ kVisitRootFlagStopLoggingNewRoots));
+ }
if ((flags & kVisitRootFlagAllRoots) != 0) {
// Argument for how root visiting deals with ArtField and ArtMethod roots.
// There is 3 GC cases to handle:
@@ -1937,7 +1978,7 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
root.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
}
- } else if ((flags & kVisitRootFlagNewRoots) != 0) {
+ } else if (!kUseReadBarrier && (flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
ObjPtr<mirror::Class> old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(visitor, RootInfo(kRootStickyClass));
@@ -1958,13 +1999,13 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
}
}
}
- if ((flags & kVisitRootFlagClearRootLog) != 0) {
+ if (!kUseReadBarrier && (flags & kVisitRootFlagClearRootLog) != 0) {
new_class_roots_.clear();
new_bss_roots_boot_oat_files_.clear();
}
- if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
+ if (!kUseReadBarrier && (flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
log_new_roots_ = true;
- } else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
+ } else if (!kUseReadBarrier && (flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
log_new_roots_ = false;
}
// We deliberately ignore the class roots in the image since we
@@ -3757,10 +3798,14 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, ObjPtr<mirror::C
}
void ClassLinker::WriteBarrierForBootOatFileBssRoots(const OatFile* oat_file) {
- WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
- DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation();
- if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) {
- new_bss_roots_boot_oat_files_.push_back(oat_file);
+ if (!kUseReadBarrier) {
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ DCHECK(!oat_file->GetBssGcRoots().empty()) << oat_file->GetLocation();
+ if (log_new_roots_ && !ContainsElement(new_bss_roots_boot_oat_files_, oat_file)) {
+ new_bss_roots_boot_oat_files_.push_back(oat_file);
+ }
+ } else {
+ LOG(FATAL) << "UNREACHABLE";
}
}
@@ -4501,6 +4546,108 @@ bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_ini
return CanWeInitializeClass(super_class, can_init_statics, can_init_parents);
}
+std::string DescribeSpace(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream oss;
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr());
+ if (cs != nullptr) {
+ if (cs->IsImageSpace()) {
+ oss << "image/" << cs->GetName() << "/" << cs->AsImageSpace()->GetImageFilename();
+ } else {
+ oss << "continuous/" << cs->GetName();
+ }
+ } else {
+ gc::space::DiscontinuousSpace* ds =
+ heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true);
+ if (ds != nullptr) {
+ oss << "discontinuous/" << ds->GetName();
+ } else {
+ oss << "invalid";
+ }
+ }
+ return oss.str();
+}
+
+std::string DescribeLoaders(ObjPtr<mirror::Class> klass, const char* iface_descriptor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream oss;
+ uint32_t hash = ComputeModifiedUtf8Hash(iface_descriptor);
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+ ObjPtr<mirror::Class> path_class_loader =
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader);
+ ObjPtr<mirror::Class> dex_class_loader =
+ soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader);
+
+ // Print the class loader chain.
+ bool found_iface;
+ const char* loader_separator = "";
+ for (ObjPtr<mirror::ClassLoader> loader = klass->GetClassLoader();
+ loader != nullptr;
+ loader = loader->GetParent()) {
+ oss << loader_separator << loader->GetClass()->PrettyDescriptor();
+ loader_separator = ";";
+ // If we didn't find the interface yet, try to find it in the current class loader.
+ if (!found_iface) {
+ ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader);
+ ObjPtr<mirror::Class> iface =
+ (table != nullptr) ? table->Lookup(iface_descriptor, hash) : nullptr;
+ if (iface != nullptr) {
+ found_iface = true;
+ oss << "[hit:" << DescribeSpace(iface) << "]";
+ }
+ }
+
+ // For PathClassLoader or DexClassLoader also dump the dex file locations.
+ if (loader->GetClass() == path_class_loader || loader->GetClass() == dex_class_loader) {
+ ArtField* const cookie_field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* const dex_file_field =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ObjPtr<mirror::Object> dex_path_list =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
+ GetObject(loader);
+ if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
+ ObjPtr<mirror::Object> dex_elements_obj =
+ jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+ GetObject(dex_path_list);
+ if (dex_elements_obj != nullptr) {
+ ObjPtr<mirror::ObjectArray<mirror::Object>> dex_elements =
+ dex_elements_obj->AsObjectArray<mirror::Object>();
+ oss << "(";
+ const char* path_separator = "";
+ for (int32_t i = 0; i != dex_elements->GetLength(); ++i) {
+ ObjPtr<mirror::Object> element = dex_elements->GetWithoutChecks(i);
+ ObjPtr<mirror::Object> dex_file =
+ (element != nullptr) ? dex_file_field->GetObject(element) : nullptr;
+ ObjPtr<mirror::LongArray> long_array =
+ (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr;
+ if (long_array != nullptr) {
+ int32_t long_array_size = long_array->GetLength();
+ // First element is the oat file.
+ for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
+ const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(
+ static_cast<uintptr_t>(long_array->GetWithoutChecks(j)));
+ oss << path_separator << cp_dex_file->GetLocation();
+ path_separator = ":";
+ }
+ }
+ }
+ oss << ")";
+ }
+ }
+ }
+ }
+
+ // Do a paranoid check that the `klass` itself is in the class table.
+ ClassTable* table =
+ Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(klass->GetClassLoader());
+ ObjPtr<mirror::Class> k = (table != nullptr) ? table->LookupByDescriptor(klass) : nullptr;
+ if (k != klass) {
+ oss << "{FAIL:" << k.Ptr() << "!=" << klass.Ptr() << "}";
+ }
+ return oss.str();
+}
+
bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
bool can_init_statics, bool can_init_parents) {
// see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
@@ -4648,7 +4795,15 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
for (size_t i = 0; i < num_direct_interfaces; i++) {
handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i));
- CHECK(handle_scope_iface != nullptr);
+ if (UNLIKELY(handle_scope_iface == nullptr)) {
+ const char* iface_descriptor =
+ klass->GetDexFile().StringByTypeIdx(klass->GetDirectInterfaceTypeIdx(i));
+ LOG(FATAL) << "Check failed: handle_scope_iface != nullptr "
+ << "Debug data for bug 34839984: "
+ << klass->PrettyDescriptor() << " iface #" << i << " " << iface_descriptor
+ << " space: " << DescribeSpace(klass.Get())
+ << " loaders: " << DescribeLoaders(klass.Get(), iface_descriptor);
+ }
CHECK(handle_scope_iface->IsInterface());
if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
// We have already done this for this interface. Skip it.
@@ -7759,7 +7914,9 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file,
uint32_t utf16_length;
const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length);
ObjPtr<mirror::String> string = intern_table_->InternStrong(utf16_length, utf8_data);
- dex_cache->SetResolvedString(string_idx, string);
+ if (string != nullptr) {
+ dex_cache->SetResolvedString(string_idx, string);
+ }
return string.Ptr();
}
@@ -7800,11 +7957,16 @@ ObjPtr<mirror::Class> ClassLinker::LookupResolvedType(const DexFile& dex_file,
// Find the class in the loaded classes table.
type = LookupClass(self, descriptor, hash, class_loader.Ptr());
}
+ if (type != nullptr) {
+ if (type->IsResolved()) {
+ dex_cache->SetResolvedType(type_idx, type);
+ } else {
+ type = nullptr;
+ }
+ }
}
- if (type != nullptr && type->IsResolved()) {
- return type.Ptr();
- }
- return nullptr;
+ DCHECK(type == nullptr || type->IsResolved());
+ return type;
}
mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
@@ -7824,6 +7986,12 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file,
Thread::PoisonObjectPointersIfDebug();
ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx);
if (resolved == nullptr) {
+ // TODO: Avoid this lookup as it duplicates work done in FindClass(). It is here
+ // as a workaround for FastNative JNI to avoid AssertNoPendingException() when
+ // trying to resolve annotations while an exception may be pending. Bug: 34659969
+ resolved = LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get());
+ }
+ if (resolved == nullptr) {
Thread* self = Thread::Current();
const char* descriptor = dex_file.StringByTypeIdx(type_idx);
resolved = FindClass(self, descriptor, class_loader);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index e27a53d15c..33eed3c8e3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -266,10 +266,6 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtField* referrer)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
-
// Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search
// for the type, since it may be referenced from but not contained within the given DexFile.
ObjPtr<mirror::Class> LookupResolvedType(const DexFile& dex_file,
@@ -277,6 +273,10 @@ class ClassLinker {
ObjPtr<mirror::DexCache> dex_cache,
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
+ static ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_idx,
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Resolve a type with the given ID from the DexFile, storing the
// result in DexCache. The ClassLoader is used to search for the
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 07f3744b69..21cdede06b 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -935,7 +935,7 @@ TEST_F(ClassLinkerTest, LookupResolvedType) {
class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
klass);
// Zero out the resolved type and make sure LookupResolvedType still finds it.
- dex_cache->SetResolvedType(type_idx, nullptr);
+ dex_cache->ClearResolvedType(type_idx);
EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
EXPECT_OBJ_PTR_EQ(
class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
@@ -970,7 +970,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) {
class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
array_klass);
// Zero out the resolved type and make sure LookupResolvedType() still finds it.
- dex_cache->SetResolvedType(array_idx, nullptr);
+ dex_cache->ClearResolvedType(array_idx);
EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr);
EXPECT_OBJ_PTR_EQ(
class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()),
@@ -993,7 +993,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) {
class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
klass.Get());
// Zero out the resolved type and make sure LookupResolvedType still finds it.
- dex_cache->SetResolvedType(type_idx, nullptr);
+ dex_cache->ClearResolvedType(type_idx);
EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
EXPECT_OBJ_PTR_EQ(
class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
@@ -1011,7 +1011,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) {
class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
klass.Get());
// Zero out the resolved type and make sure LookupResolvedType() still finds it.
- dex_cache->SetResolvedType(type_idx, nullptr);
+ dex_cache->ClearResolvedType(type_idx);
EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
EXPECT_OBJ_PTR_EQ(
class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 17e3729a20..d7abe2a8f4 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -223,9 +223,9 @@ class CheckJniAbortCatcher {
return; \
}
-#define TEST_DISABLED_FOR_READ_BARRIER_ON_X86() \
- if (kUseReadBarrier && kRuntimeISA == kX86) { \
- printf("WARNING: TEST DISABLED FOR READ BARRIER ON X86\n"); \
+#define TEST_DISABLED_FOR_X86() \
+ if (kRuntimeISA == kX86) { \
+ printf("WARNING: TEST DISABLED FOR X86\n"); \
return; \
}
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index a95f94cabb..d39ea35a90 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -299,6 +299,7 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint
return result.GetL();
}
+template <bool kTransactionActive>
bool ProcessAnnotationValue(Handle<mirror::Class> klass,
const uint8_t** annotation_ptr,
DexFile::AnnotationValue* annotation_value,
@@ -409,22 +410,21 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass,
}
PointerSize pointer_size = class_linker->GetImagePointerSize();
set_object = true;
- DCHECK(!Runtime::Current()->IsActiveTransaction());
if (method->IsConstructor()) {
if (pointer_size == PointerSize::k64) {
element_object = mirror::Constructor::CreateFromArtMethod<PointerSize::k64,
- false>(self, method);
+ kTransactionActive>(self, method);
} else {
element_object = mirror::Constructor::CreateFromArtMethod<PointerSize::k32,
- false>(self, method);
+ kTransactionActive>(self, method);
}
} else {
if (pointer_size == PointerSize::k64) {
element_object = mirror::Method::CreateFromArtMethod<PointerSize::k64,
- false>(self, method);
+ kTransactionActive>(self, method);
} else {
element_object = mirror::Method::CreateFromArtMethod<PointerSize::k32,
- false>(self, method);
+ kTransactionActive>(self, method);
}
}
if (element_object == nullptr) {
@@ -449,9 +449,11 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass,
set_object = true;
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
if (pointer_size == PointerSize::k64) {
- element_object = mirror::Field::CreateFromArtField<PointerSize::k64>(self, field, true);
+ element_object = mirror::Field::CreateFromArtField<PointerSize::k64,
+ kTransactionActive>(self, field, true);
} else {
- element_object = mirror::Field::CreateFromArtField<PointerSize::k32>(self, field, true);
+ element_object = mirror::Field::CreateFromArtField<PointerSize::k32,
+ kTransactionActive>(self, field, true);
}
if (element_object == nullptr) {
return false;
@@ -497,45 +499,49 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass,
}
DexFile::AnnotationValue new_annotation_value;
for (uint32_t i = 0; i < size; ++i) {
- if (!ProcessAnnotationValue(klass, &annotation, &new_annotation_value,
- component_type, DexFile::kPrimitivesOrObjects)) {
+ if (!ProcessAnnotationValue<kTransactionActive>(klass,
+ &annotation,
+ &new_annotation_value,
+ component_type,
+ DexFile::kPrimitivesOrObjects)) {
return false;
}
if (!component_type->IsPrimitive()) {
mirror::Object* obj = new_annotation_value.value_.GetL();
- new_array->AsObjectArray<mirror::Object>()->SetWithoutChecks<false>(i, obj);
+ new_array->AsObjectArray<mirror::Object>()->
+ SetWithoutChecks<kTransactionActive>(i, obj);
} else {
switch (new_annotation_value.type_) {
case DexFile::kDexAnnotationByte:
- new_array->AsByteArray()->SetWithoutChecks<false>(
+ new_array->AsByteArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetB());
break;
case DexFile::kDexAnnotationShort:
- new_array->AsShortArray()->SetWithoutChecks<false>(
+ new_array->AsShortArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetS());
break;
case DexFile::kDexAnnotationChar:
- new_array->AsCharArray()->SetWithoutChecks<false>(
+ new_array->AsCharArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetC());
break;
case DexFile::kDexAnnotationInt:
- new_array->AsIntArray()->SetWithoutChecks<false>(
+ new_array->AsIntArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetI());
break;
case DexFile::kDexAnnotationLong:
- new_array->AsLongArray()->SetWithoutChecks<false>(
+ new_array->AsLongArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetJ());
break;
case DexFile::kDexAnnotationFloat:
- new_array->AsFloatArray()->SetWithoutChecks<false>(
+ new_array->AsFloatArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetF());
break;
case DexFile::kDexAnnotationDouble:
- new_array->AsDoubleArray()->SetWithoutChecks<false>(
+ new_array->AsDoubleArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetD());
break;
case DexFile::kDexAnnotationBoolean:
- new_array->AsBooleanArray()->SetWithoutChecks<false>(
+ new_array->AsBooleanArray()->SetWithoutChecks<kTransactionActive>(
i, new_annotation_value.value_.GetZ());
break;
default:
@@ -611,8 +617,11 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass,
annotation_method->GetReturnType(true /* resolve */)));
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return,
- DexFile::kAllObjects)) {
+ if (!ProcessAnnotationValue<false>(klass,
+ annotation,
+ &annotation_value,
+ method_return,
+ DexFile::kAllObjects)) {
return nullptr;
}
Handle<mirror::Object> value_object(hs.NewHandle(annotation_value.value_.GetL()));
@@ -716,8 +725,18 @@ mirror::Object* GetAnnotationValue(Handle<mirror::Class> klass,
return nullptr;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue(klass, &annotation, &annotation_value, array_class,
- DexFile::kAllObjects)) {
+ bool result = Runtime::Current()->IsActiveTransaction()
+ ? ProcessAnnotationValue<true>(klass,
+ &annotation,
+ &annotation_value,
+ array_class,
+ DexFile::kAllObjects)
+ : ProcessAnnotationValue<false>(klass,
+ &annotation,
+ &annotation_value,
+ array_class,
+ DexFile::kAllObjects);
+ if (!result) {
return nullptr;
}
if (annotation_value.type_ != expected_type) {
@@ -949,8 +968,11 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
- if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type,
- DexFile::kAllObjects)) {
+ if (!ProcessAnnotationValue<false>(h_klass,
+ &annotation,
+ &annotation_value,
+ return_type,
+ DexFile::kAllObjects)) {
return nullptr;
}
return annotation_value.value_.GetL();
@@ -1201,8 +1223,11 @@ mirror::Class* GetEnclosingClass(Handle<mirror::Class> klass) {
return nullptr;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue(klass, &annotation, &annotation_value,
- ScopedNullHandle<mirror::Class>(), DexFile::kAllRaw)) {
+ if (!ProcessAnnotationValue<false>(klass,
+ &annotation,
+ &annotation_value,
+ ScopedNullHandle<mirror::Class>(),
+ DexFile::kAllRaw)) {
return nullptr;
}
if (annotation_value.type_ != DexFile::kDexAnnotationMethod) {
@@ -1252,9 +1277,11 @@ bool GetInnerClass(Handle<mirror::Class> klass, mirror::String** name) {
return false;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue(klass, &annotation, &annotation_value,
- ScopedNullHandle<mirror::Class>(),
- DexFile::kAllObjects)) {
+ if (!ProcessAnnotationValue<false>(klass,
+ &annotation,
+ &annotation_value,
+ ScopedNullHandle<mirror::Class>(),
+ DexFile::kAllObjects)) {
return false;
}
if (annotation_value.type_ != DexFile::kDexAnnotationNull &&
@@ -1283,8 +1310,11 @@ bool GetInnerClassFlags(Handle<mirror::Class> klass, uint32_t* flags) {
return false;
}
DexFile::AnnotationValue annotation_value;
- if (!ProcessAnnotationValue(klass, &annotation, &annotation_value,
- ScopedNullHandle<mirror::Class>(), DexFile::kAllRaw)) {
+ if (!ProcessAnnotationValue<false>(klass,
+ &annotation,
+ &annotation_value,
+ ScopedNullHandle<mirror::Class>(),
+ DexFile::kAllRaw)) {
return false;
}
if (annotation_value.type_ != DexFile::kDexAnnotationInt) {
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 69c6151d9e..51678699b4 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -70,6 +70,11 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
// rather than use this flag.
args.push_back("-Xnorelocate");
+ ScratchFile profile_file;
+ if (CompilerFilter::DependsOnProfile(filter)) {
+ args.push_back("--profile-file=" + profile_file.GetFilename());
+ }
+
if (pic) {
args.push_back("--compile-pic");
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 28aca6c905..3bc49b8506 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -709,10 +709,10 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx,
return resolved_method;
} else if (type == kSuper) {
// TODO This lookup is rather slow.
- dex::TypeIndex method_type_idx =
- referrer->GetDexFile()->GetMethodId(method_idx).class_idx_;
- mirror::Class* method_reference_class =
- referrer->GetDexCache()->GetResolvedType(method_type_idx);
+ ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+ dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_;
+ ObjPtr<mirror::Class> method_reference_class = ClassLinker::LookupResolvedType(
+ method_type_idx, dex_cache, referrer->GetClassLoader());
if (method_reference_class == nullptr) {
// Need to do full type resolution...
return nullptr;
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 699cf91c70..47c6b514d5 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -32,23 +32,33 @@
namespace art {
static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) {
- // For AOT code, we need a write barrier for the class loader that holds
- // the GC roots in the .bss.
- const DexFile* dex_file = outer_method->GetDexFile();
+ // For non-CC AOT code, we need a write barrier for the class loader that holds the
+ // GC roots in the .bss. For CC, we do not need to do anything because the roots
+ // we're storing are all referencing to-space and do not need to be re-visited.
+ // However, we do the DCHECK() for the registration of oat files with .bss sections.
+ const DexFile* dex_file =
+ (kUseReadBarrier && !kIsDebugBuild) ? nullptr : outer_method->GetDexFile();
if (dex_file != nullptr &&
dex_file->GetOatDexFile() != nullptr &&
!dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) {
- mirror::ClassLoader* class_loader = outer_method->GetClassLoader();
- if (class_loader != nullptr) {
- DCHECK(!class_loader->GetClassTable()->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile()))
+ ObjPtr<mirror::ClassLoader> class_loader = outer_method->GetClassLoader();
+ if (kIsDebugBuild) {
+ ClassTable* class_table =
+ Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader);
+ CHECK(class_table != nullptr &&
+ !class_table->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile()))
<< "Oat file with .bss GC roots was not registered in class table: "
<< dex_file->GetOatDexFile()->GetOatFile()->GetLocation();
- // Note that we emit the barrier before the compiled code stores the String or Class
- // as a GC root. This is OK as there is no suspend point point in between.
- Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
- } else {
- Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(
- dex_file->GetOatDexFile()->GetOatFile());
+ }
+ if (!kUseReadBarrier) {
+ if (class_loader != nullptr) {
+ // Note that we emit the barrier before the compiled code stores the String or Class
+ // as a GC root. This is OK as there is no suspend point point in between.
+ Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
+ } else {
+ Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(
+ dex_file->GetOatDexFile()->GetOatFile());
+ }
}
}
}
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 7014357527..eef4fba20d 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -55,6 +55,8 @@ enum CollectorType {
kCollectorTypeClassLinker,
// JIT Code cache fake collector.
kCollectorTypeJitCodeCache,
+ // Hprof fake collector.
+ kCollectorTypeHprof,
// Fake collector for installing/removing a system-weak holder.
kCollectorTypeAddRemoveSystemWeakHolder,
};
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 7ff845d98e..9e34346686 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -39,6 +39,7 @@ const char* PrettyCause(GcCause cause) {
case kGcCauseClassLinker: return "ClassLinker";
case kGcCauseJitCodeCache: return "JitCodeCache";
case kGcCauseAddRemoveSystemWeakHolder: return "SystemWeakHolder";
+ case kGcCauseHprof: return "Hprof";
}
LOG(FATAL) << "Unreachable";
UNREACHABLE();
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index f54f0e4901..9b285b12a4 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -53,6 +53,8 @@ enum GcCause {
kGcCauseJitCodeCache,
// Not a real GC cause, used to add or remove system-weak holders.
kGcCauseAddRemoveSystemWeakHolder,
+ // Not a real GC cause, used to hprof running in the middle of GC.
+ kGcCauseHprof,
};
const char* PrettyCause(GcCause cause);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index a78de37796..34afa2aa7e 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -29,6 +29,7 @@
#include "base/arena_allocator.h"
#include "base/dumpable.h"
#include "base/histogram-inl.h"
+#include "base/memory_tool.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
@@ -188,6 +189,7 @@ Heap::Heap(size_t initial_size,
disable_thread_flip_count_(0),
thread_flip_running_(false),
collector_type_running_(kCollectorTypeNone),
+ thread_running_gc_(nullptr),
last_gc_type_(collector::kGcTypeNone),
next_gc_type_(collector::kGcTypePartial),
capacity_(capacity),
@@ -286,7 +288,7 @@ Heap::Heap(size_t initial_size,
if (foreground_collector_type_ == kCollectorTypeCC) {
// Need to use a low address so that we can allocate a contiguous
// 2 * Xmx space when there's no image (dex2oat for target).
-#if defined(__LP64__)
+#if defined(__LP64__) || !defined(ADDRESS_SANITIZER)
CHECK_GE(300 * MB, non_moving_space_capacity);
requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity;
#else
@@ -367,7 +369,7 @@ Heap::Heap(size_t initial_size,
&error_str));
CHECK(non_moving_space_mem_map != nullptr) << error_str;
// Try to reserve virtual memory at a lower address if we have a separate non moving space.
-#if defined(__LP64__)
+#if defined(__LP64__) || !defined(ADDRESS_SANITIZER)
request_begin = reinterpret_cast<uint8_t*>(300 * MB);
#else
// For 32-bit, use 0x20000000 because asan reserves 0x04000000 - 0x20000000.
@@ -935,7 +937,14 @@ void Heap::VisitObjectsInternalRegionSpace(ObjectCallback callback, void* arg) {
// calls VerifyHeapReferences() as part of the zygote compaction
// which then would call here without the moving GC disabled,
// which is fine.
- DCHECK(IsMovingGCDisabled(self));
+ bool is_thread_running_gc = false;
+ if (kIsDebugBuild) {
+ MutexLock mu(self, *gc_complete_lock_);
+ is_thread_running_gc = self == thread_running_gc_;
+ }
+ // If we are not the thread running the GC on in a GC exclusive region, then moving GC
+ // must be disabled.
+ DCHECK(is_thread_running_gc || IsMovingGCDisabled(self));
}
region_space_->Walk(callback, arg);
}
@@ -1386,6 +1395,7 @@ void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) {
// Ensure there is only one GC at a time.
WaitForGcToCompleteLocked(cause, self);
collector_type_running_ = collector_type;
+ thread_running_gc_ = self;
}
void Heap::TrimSpaces(Thread* self) {
@@ -2785,6 +2795,7 @@ void Heap::FinishGC(Thread* self, collector::GcType gc_type) {
}
// Reset.
running_collection_is_blocking_ = false;
+ thread_running_gc_ = nullptr;
// Wake anyone who may have been waiting for the GC to complete.
gc_complete_cond_->Broadcast(self);
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index a4d300b110..1a782b409b 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1189,6 +1189,9 @@ class Heap {
// True while the garbage collector is running.
volatile CollectorType collector_type_running_ GUARDED_BY(gc_complete_lock_);
+ // The thread currently running the GC.
+ volatile Thread* thread_running_gc_ GUARDED_BY(gc_complete_lock_);
+
// Last Gc type we ran. Used by WaitForConcurrentGc to know which Gc was waited on.
volatile collector::GcType last_gc_type_ GUARDED_BY(gc_complete_lock_);
collector::GcType next_gc_type_;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 2163a20e87..010ef1156a 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1237,9 +1237,9 @@ class ImageSpaceLoader {
}
dex_cache->FixupStrings<kWithoutReadBarrier>(new_strings, fixup_adapter);
}
- GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
+ mirror::TypeDexCacheType* types = dex_cache->GetResolvedTypes();
if (types != nullptr) {
- GcRoot<mirror::Class>* new_types = fixup_adapter.ForwardObject(types);
+ mirror::TypeDexCacheType* new_types = fixup_adapter.ForwardObject(types);
if (types != new_types) {
dex_cache->SetResolvedTypes(new_types);
}
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index f390645a32..e59c4bb28e 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -50,6 +50,7 @@
#include "gc_root.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/allocation_record.h"
+#include "gc/scoped_gc_critical_section.h"
#include "gc/heap.h"
#include "gc/space/space.h"
#include "globals.h"
@@ -1449,22 +1450,15 @@ void Hprof::VisitRoot(mirror::Object* obj, const RootInfo& info) {
// Otherwise, "filename" is used to create an output file.
void DumpHeap(const char* filename, int fd, bool direct_to_ddms) {
CHECK(filename != nullptr);
-
Thread* self = Thread::Current();
- gc::Heap* heap = Runtime::Current()->GetHeap();
- if (heap->IsGcConcurrentAndMoving()) {
- // Need to take a heap dump while GC isn't running. See the
- // comment in Heap::VisitObjects().
- heap->IncrementDisableMovingGC(self);
- }
- {
- ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */);
- Hprof hprof(filename, fd, direct_to_ddms);
- hprof.Dump();
- }
- if (heap->IsGcConcurrentAndMoving()) {
- heap->DecrementDisableMovingGC(self);
- }
+ // Need to take a heap dump while GC isn't running. See the comment in Heap::VisitObjects().
+ // Also we need the critical section to avoid visiting the same object twice. See b/34967844
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseHprof,
+ gc::kCollectorTypeHprof);
+ ScopedSuspendAll ssa(__FUNCTION__, true /* long suspend */);
+ Hprof hprof(filename, fd, direct_to_ddms);
+ hprof.Dump();
}
} // namespace hprof
diff --git a/runtime/image.cc b/runtime/image.cc
index 54b099eb14..4e6da79205 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '6', '\0' }; // Erroneous resolved class.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '8', '\0' }; // hash-based DexCache types
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 66f14b99d8..af0478c1eb 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -175,56 +175,61 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz
return param->AsString();
}
-void UnstartedRuntime::UnstartedClassForName(
- Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
+ ShadowFrame* shadow_frame,
+ JValue* result,
+ size_t arg_offset,
+ bool long_form,
+ const char* caller) {
mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
if (class_name == nullptr) {
return;
}
+ bool initialize_class;
+ mirror::ClassLoader* class_loader;
+ if (long_form) {
+ initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0;
+ class_loader = down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2));
+ } else {
+ initialize_class = true;
+ // TODO: This is really only correct for the boot classpath, and for robustness we should
+ // check the caller.
+ class_loader = nullptr;
+ }
+
+ ScopedObjectAccessUnchecked soa(self);
+ if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(soa, class_loader)) {
+ AbortTransactionOrFail(self,
+ "Only the boot classloader is supported: %s",
+ mirror::Object::PrettyTypeOf(class_loader).c_str());
+ return;
+ }
+
StackHandleScope<1> hs(self);
Handle<mirror::String> h_class_name(hs.NewHandle(class_name));
UnstartedRuntimeFindClass(self,
h_class_name,
ScopedNullHandle<mirror::ClassLoader>(),
result,
- "Class.forName",
- true,
+ caller,
+ initialize_class,
false);
CheckExceptionGenerateClassNotFound(self);
}
+void UnstartedRuntime::UnstartedClassForName(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, false, "Class.forName");
+}
+
void UnstartedRuntime::UnstartedClassForNameLong(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
- mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
- if (class_name == nullptr) {
- return;
- }
- bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0;
- mirror::ClassLoader* class_loader =
- down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2));
- StackHandleScope<2> hs(self);
- Handle<mirror::String> h_class_name(hs.NewHandle(class_name));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader));
- UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, "Class.forName",
- initialize_class, false);
- CheckExceptionGenerateClassNotFound(self);
+ UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.forName");
}
void UnstartedRuntime::UnstartedClassClassForName(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
- mirror::String* class_name = GetClassName(self, shadow_frame, arg_offset);
- if (class_name == nullptr) {
- return;
- }
- bool initialize_class = shadow_frame->GetVReg(arg_offset + 1) != 0;
- mirror::ClassLoader* class_loader =
- down_cast<mirror::ClassLoader*>(shadow_frame->GetVRegReference(arg_offset + 2));
- StackHandleScope<2> hs(self);
- Handle<mirror::String> h_class_name(hs.NewHandle(class_name));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader));
- UnstartedRuntimeFindClass(self, h_class_name, h_class_loader, result, "Class.classForName",
- initialize_class, false);
- CheckExceptionGenerateClassNotFound(self);
+ UnstartedClassForNameCommon(self, shadow_frame, result, arg_offset, true, "Class.classForName");
}
void UnstartedRuntime::UnstartedClassNewInstance(
@@ -440,6 +445,20 @@ void UnstartedRuntime::UnstartedClassGetInnerClassFlags(
result->SetI(mirror::Class::GetInnerClassFlags(klass, default_value));
}
+void UnstartedRuntime::UnstartedClassGetSignatureAnnotation(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> klass(hs.NewHandle(
+ reinterpret_cast<mirror::Class*>(shadow_frame->GetVRegReference(arg_offset))));
+
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ result->SetL(nullptr);
+ return;
+ }
+
+ result->SetL(annotations::GetSignatureAnnotationForClass(klass));
+}
+
void UnstartedRuntime::UnstartedClassIsAnonymousClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
StackHandleScope<1> hs(self);
@@ -613,6 +632,72 @@ void UnstartedRuntime::UnstartedClassLoaderGetResourceAsStream(
GetResourceAsStream(self, shadow_frame, result, arg_offset);
}
+void UnstartedRuntime::UnstartedConstructorNewInstance0(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ // This is a cutdown version of java_lang_reflect_Constructor.cc's implementation.
+ StackHandleScope<4> hs(self);
+ Handle<mirror::Constructor> m = hs.NewHandle(
+ reinterpret_cast<mirror::Constructor*>(shadow_frame->GetVRegReference(arg_offset)));
+ Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
+ reinterpret_cast<mirror::ObjectArray<mirror::Object>*>(
+ shadow_frame->GetVRegReference(arg_offset + 1)));
+ Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass()));
+ if (UNLIKELY(c->IsAbstract())) {
+ AbortTransactionOrFail(self, "Cannot handle abstract classes");
+ return;
+ }
+ // Verify that we can access the class.
+ if (!m->IsAccessible() && !c->IsPublic()) {
+ // Go 2 frames back, this method is always called from newInstance0, which is called from
+ // Constructor.newInstance(Object... args).
+ ObjPtr<mirror::Class> caller = GetCallingClass(self, 2);
+ // If caller is null, then we called from JNI, just avoid the check since JNI avoids most
+ // access checks anyways. TODO: Investigate if this the correct behavior.
+ if (caller != nullptr && !caller->CanAccess(c.Get())) {
+ AbortTransactionOrFail(self, "Cannot access class");
+ return;
+ }
+ }
+ if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, c, true, true)) {
+ DCHECK(self->IsExceptionPending());
+ return;
+ }
+ if (c->IsClassClass()) {
+ AbortTransactionOrFail(self, "new Class() is not supported");
+ return;
+ }
+
+ // String constructor is replaced by a StringFactory method in InvokeMethod.
+ if (c->IsStringClass()) {
+ // We don't support strings.
+ AbortTransactionOrFail(self, "String construction is not supported");
+ return;
+ }
+
+ Handle<mirror::Object> receiver = hs.NewHandle(c->AllocObject(self));
+ if (receiver == nullptr) {
+ AbortTransactionOrFail(self, "Could not allocate");
+ return;
+ }
+
+ // It's easier to use reflection to make the call, than create the uint32_t array.
+ {
+ ScopedObjectAccessUnchecked soa(self);
+ ScopedLocalRef<jobject> method_ref(self->GetJniEnv(),
+ soa.AddLocalReference<jobject>(m.Get()));
+ ScopedLocalRef<jobject> object_ref(self->GetJniEnv(),
+ soa.AddLocalReference<jobject>(receiver.Get()));
+ ScopedLocalRef<jobject> args_ref(self->GetJniEnv(),
+ soa.AddLocalReference<jobject>(args.Get()));
+ InvokeMethod(soa, method_ref.get(), object_ref.get(), args_ref.get(), 2);
+ }
+ if (self->IsExceptionPending()) {
+ AbortTransactionOrFail(self, "Failed running constructor");
+ } else {
+ result->SetL(receiver.Get());
+ }
+}
+
void UnstartedRuntime::UnstartedVmClassLoaderFindLoadedClass(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
mirror::String* class_name = shadow_frame->GetVRegReference(arg_offset + 1)->AsString();
diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h
index 3f36a27e92..bc9ead8360 100644
--- a/runtime/interpreter/unstarted_runtime.h
+++ b/runtime/interpreter/unstarted_runtime.h
@@ -89,6 +89,13 @@ class UnstartedRuntime {
#undef UNSTARTED_RUNTIME_JNI_LIST
#undef UNSTARTED_JNI
+ static void UnstartedClassForNameCommon(Thread* self,
+ ShadowFrame* shadow_frame,
+ JValue* result,
+ size_t arg_offset,
+ bool long_form,
+ const char* caller) REQUIRES_SHARED(Locks::mutator_lock_);
+
static void InitializeInvokeHandlers();
static void InitializeJNIHandlers();
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 929b747840..6fc7989acf 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -31,8 +31,10 @@
V(ClassGetDeclaringClass, "java.lang.Class java.lang.Class.getDeclaringClass()") \
V(ClassGetEnclosingClass, "java.lang.Class java.lang.Class.getEnclosingClass()") \
V(ClassGetInnerClassFlags, "int java.lang.Class.getInnerClassFlags(int)") \
+ V(ClassGetSignatureAnnotation, "java.lang.String[] java.lang.Class.getSignatureAnnotation()") \
V(ClassIsAnonymousClass, "boolean java.lang.Class.isAnonymousClass()") \
V(ClassLoaderGetResourceAsStream, "java.io.InputStream java.lang.ClassLoader.getResourceAsStream(java.lang.String)") \
+ V(ConstructorNewInstance0, "java.lang.Object java.lang.reflect.Constructor.newInstance0(java.lang.Object[])") \
V(VmClassLoaderFindLoadedClass, "java.lang.Class java.lang.VMClassLoader.findLoadedClass(java.lang.ClassLoader, java.lang.String)") \
V(VoidLookupType, "java.lang.Class java.lang.Void.lookupType()") \
V(SystemArraycopy, "void java.lang.System.arraycopy(java.lang.Object, int, java.lang.Object, int, int)") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 98a17e41f9..db222fad6d 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -29,6 +29,8 @@
#include "handle_scope-inl.h"
#include "interpreter/interpreter_common.h"
#include "mirror/class_loader.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object-inl.h"
#include "mirror/string-inl.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -1077,12 +1079,293 @@ TEST_F(UnstartedRuntimeTest, LogManager) {
StackHandleScope<1> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> log_manager_class = hs.NewHandle(
- class_linker->FindClass(self,
- "Ljava/util/logging/LogManager;",
- ScopedNullHandle<mirror::ClassLoader>()));
+ class_linker->FindClass(self,
+ "Ljava/util/logging/LogManager;",
+ ScopedNullHandle<mirror::ClassLoader>()));
ASSERT_TRUE(log_manager_class.Get() != nullptr);
ASSERT_TRUE(class_linker->EnsureInitialized(self, log_manager_class, true, true));
}
+class UnstartedClassForNameTest : public UnstartedRuntimeTest {
+ public:
+ template <typename T>
+ void RunTest(T& runner, bool in_transaction, bool should_succeed) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ // Ensure that Class is initialized.
+ {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class = hs.NewHandle(mirror::Class::GetJavaLangClass());
+ CHECK(class_linker->EnsureInitialized(self, h_class, true, true));
+ }
+
+ // A selection of classes from different core classpath components.
+ constexpr const char* kTestCases[] = {
+ "java.net.CookieManager", // From libcore.
+ "dalvik.system.ClassExt", // From libart.
+ };
+
+ if (in_transaction) {
+ // For transaction mode, we cannot load any classes, as the pre-fence initialization of
+ // classes isn't transactional. Load them ahead of time.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ for (const char* name : kTestCases) {
+ class_linker->FindClass(self,
+ DotToDescriptor(name).c_str(),
+ ScopedNullHandle<mirror::ClassLoader>());
+ CHECK(!self->IsExceptionPending()) << self->GetException()->Dump();
+ }
+ }
+
+ if (!should_succeed) {
+ // Negative test. In general, currentThread should fail (as we should not leak a peer that will
+ // be recreated at runtime).
+ PrepareForAborts();
+ }
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ for (const char* name : kTestCases) {
+ mirror::String* name_string = mirror::String::AllocFromModifiedUtf8(self, name);
+ CHECK(name_string != nullptr);
+
+ Transaction transaction;
+ if (in_transaction) {
+ Runtime::Current()->EnterTransactionMode(&transaction);
+ }
+ CHECK(!self->IsExceptionPending());
+
+ runner(self, shadow_frame, name_string, &result);
+
+ if (in_transaction) {
+ Runtime::Current()->ExitTransactionMode();
+ }
+
+ if (should_succeed) {
+ CHECK(!self->IsExceptionPending()) << name << " " << self->GetException()->Dump();
+ CHECK(result.GetL() != nullptr) << name;
+ } else {
+ CHECK(self->IsExceptionPending()) << name;
+ if (in_transaction) {
+ ASSERT_TRUE(transaction.IsAborted());
+ }
+ self->ClearException();
+ }
+ }
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+ }
+
+ mirror::ClassLoader* GetBootClassLoader() REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
+ StackHandleScope<2> hs(self);
+ MutableHandle<mirror::ClassLoader> boot_cp = hs.NewHandle<mirror::ClassLoader>(nullptr);
+
+ {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ // Create the fake boot classloader. Any instance is fine, they are technically interchangeable.
+ Handle<mirror::Class> boot_cp_class = hs.NewHandle(
+ class_linker->FindClass(self,
+ "Ljava/lang/BootClassLoader;",
+ ScopedNullHandle<mirror::ClassLoader>()));
+ CHECK(boot_cp_class != nullptr);
+ CHECK(class_linker->EnsureInitialized(self, boot_cp_class, true, true));
+
+ boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader());
+ CHECK(boot_cp != nullptr);
+
+ ArtMethod* boot_cp_init = boot_cp_class->FindDeclaredDirectMethod(
+ "<init>", "()V", class_linker->GetImagePointerSize());
+ CHECK(boot_cp_init != nullptr);
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, boot_cp_init, 0);
+ shadow_frame->SetVRegReference(0, boot_cp.Get());
+
+ // create instruction data for invoke-direct {v0} of method with fake index
+ uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 };
+ const Instruction* inst = Instruction::At(inst_data);
+
+ interpreter::DoCall<false, false>(boot_cp_init,
+ self,
+ *shadow_frame,
+ inst,
+ inst_data[0],
+ &result);
+ CHECK(!self->IsExceptionPending());
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+ }
+
+ return boot_cp.Get();
+ }
+};
+
+TEST_F(UnstartedClassForNameTest, ClassForName) {
+ auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame->SetVRegReference(0, name);
+ UnstartedClassForName(self, shadow_frame, result, 0);
+ };
+ RunTest(runner, false, true);
+}
+
+TEST_F(UnstartedClassForNameTest, ClassForNameLong) {
+ auto runner = [](Thread* self, ShadowFrame* shadow_frame, mirror::String* name, JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame->SetVRegReference(0, name);
+ shadow_frame->SetVReg(1, 0);
+ shadow_frame->SetVRegReference(2, nullptr);
+ UnstartedClassForNameLong(self, shadow_frame, result, 0);
+ };
+ RunTest(runner, false, true);
+}
+
+TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoader) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader());
+
+ auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame->SetVRegReference(0, name);
+ shadow_frame->SetVReg(1, 0);
+ shadow_frame->SetVRegReference(2, boot_cp.Get());
+ UnstartedClassForNameLong(th, shadow_frame, result, 0);
+ };
+ RunTest(runner, false, true);
+}
+
+TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderTransaction) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> boot_cp = hs.NewHandle(GetBootClassLoader());
+
+ auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame->SetVRegReference(0, name);
+ shadow_frame->SetVReg(1, 0);
+ shadow_frame->SetVRegReference(2, boot_cp.Get());
+ UnstartedClassForNameLong(th, shadow_frame, result, 0);
+ };
+ RunTest(runner, true, true);
+}
+
+TEST_F(UnstartedClassForNameTest, ClassForNameLongWithClassLoaderFail) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ StackHandleScope<2> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ jobject path_jobj = class_linker->CreatePathClassLoader(self, {});
+ ASSERT_TRUE(path_jobj != nullptr);
+ Handle<mirror::ClassLoader> path_cp = hs.NewHandle<mirror::ClassLoader>(
+ self->DecodeJObject(path_jobj)->AsClassLoader());
+
+ auto runner = [&](Thread* th, ShadowFrame* shadow_frame, mirror::String* name, JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame->SetVRegReference(0, name);
+ shadow_frame->SetVReg(1, 0);
+ shadow_frame->SetVRegReference(2, path_cp.Get());
+ UnstartedClassForNameLong(th, shadow_frame, result, 0);
+ };
+ RunTest(runner, true, false);
+}
+
+TEST_F(UnstartedRuntimeTest, ClassGetSignatureAnnotation) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ StackHandleScope<1> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> list_class = hs.NewHandle(
+ class_linker->FindClass(self,
+ "Ljava/util/List;",
+ ScopedNullHandle<mirror::ClassLoader>()));
+ ASSERT_TRUE(list_class.Get() != nullptr);
+ ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true));
+
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+ shadow_frame->SetVRegReference(0, list_class.Get());
+ UnstartedClassGetSignatureAnnotation(self, shadow_frame, &result, 0);
+ ASSERT_TRUE(result.GetL() != nullptr);
+ ASSERT_FALSE(self->IsExceptionPending());
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+
+ ASSERT_TRUE(result.GetL()->IsObjectArray());
+ ObjPtr<mirror::ObjectArray<mirror::Object>> array =
+ result.GetL()->AsObjectArray<mirror::Object>();
+ std::ostringstream oss;
+ for (int32_t i = 0; i != array->GetLength(); ++i) {
+ ObjPtr<mirror::Object> elem = array->Get(i);
+ ASSERT_TRUE(elem != nullptr);
+ ASSERT_TRUE(elem->IsString());
+ oss << elem->AsString()->ToModifiedUtf8();
+ }
+ std::string output_string = oss.str();
+ ASSERT_EQ(output_string, "<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;");
+}
+
+TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+
+ StackHandleScope<4> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+
+ // Get Throwable.
+ Handle<mirror::Class> throw_class = hs.NewHandle(mirror::Throwable::GetJavaLangThrowable());
+ ASSERT_TRUE(class_linker->EnsureInitialized(self, throw_class, true, true));
+
+ // Get an input object.
+ Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd"));
+
+ // Find the constructor.
+ ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod(
+ "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
+ ASSERT_TRUE(throw_cons != nullptr);
+
+ Handle<mirror::Constructor> cons = hs.NewHandle(
+ mirror::Constructor::CreateFromArtMethod<kRuntimePointerSize, false>(self, throw_cons));
+ ASSERT_TRUE(cons != nullptr);
+
+ Handle<mirror::ObjectArray<mirror::Object>> args = hs.NewHandle(
+ class_linker->AllocObjectArray<mirror::Object>(self, 1));
+ ASSERT_TRUE(args != nullptr);
+ args->Set(0, input.Get());
+
+ // OK, we're ready now.
+ JValue result;
+ ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+ shadow_frame->SetVRegReference(0, cons.Get());
+ shadow_frame->SetVRegReference(1, args.Get());
+ UnstartedConstructorNewInstance0(self, shadow_frame, &result, 0);
+
+ ASSERT_TRUE(result.GetL() != nullptr);
+ ASSERT_FALSE(self->IsExceptionPending());
+
+ // Should be a new object.
+ ASSERT_NE(result.GetL(), input.Get());
+ // Should be a String.
+ ASSERT_EQ(mirror::Throwable::GetJavaLangThrowable(), result.GetL()->GetClass());
+ // Should have the right string.
+ ObjPtr<mirror::String> result_msg =
+ reinterpret_cast<mirror::Throwable*>(result.GetL())->GetDetailMessage();
+ EXPECT_EQ(input.Get(), result_msg.Ptr());
+
+ ShadowFrame::DeleteDeoptimizedFrame(shadow_frame);
+}
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 60ab275641..c226a38299 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1245,15 +1245,40 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S
}
void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations,
- std::vector<MethodReference>& methods) {
+ std::vector<ProfileMethodInfo>& methods) {
ScopedTrace trace(__FUNCTION__);
MutexLock mu(Thread::Current(), lock_);
for (const ProfilingInfo* info : profiling_infos_) {
ArtMethod* method = info->GetMethod();
const DexFile* dex_file = method->GetDexFile();
- if (ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
- methods.emplace_back(dex_file, method->GetDexMethodIndex());
+ if (!ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) {
+ // Skip dex files which are not profiled.
+ continue;
}
+ std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches;
+ for (size_t i = 0; i < info->number_of_inline_caches_; ++i) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes;
+ const InlineCache& cache = info->cache_[i];
+ for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) {
+ mirror::Class* cls = cache.classes_[k].Read();
+ if (cls == nullptr) {
+ break;
+ }
+ const DexFile& class_dex_file = cls->GetDexFile();
+ dex::TypeIndex type_index = cls->GetDexTypeIndex();
+ if (ContainsElement(dex_base_locations, class_dex_file.GetBaseLocation())) {
+ // Only consider classes from the same apk (including multidex).
+ profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/
+ &class_dex_file, type_index);
+ }
+ }
+ if (!profile_classes.empty()) {
+ inline_caches.emplace_back(/*ProfileMethodInfo::ProfileInlineCache*/
+ cache.dex_pc_, profile_classes);
+ }
+ }
+ methods.emplace_back(/*ProfileMethodInfo*/
+ dex_file, method->GetDexMethodIndex(), inline_caches);
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index b5e31769ab..33a792fe75 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -30,6 +30,7 @@
#include "method_reference.h"
#include "oat_file.h"
#include "object_callbacks.h"
+#include "profile_compilation_info.h"
#include "safe_map.h"
#include "thread_pool.h"
@@ -192,7 +193,7 @@ class JitCodeCache {
// Adds to `methods` all profiled methods which are part of any of the given dex locations.
void GetProfiledMethods(const std::set<std::string>& dex_base_locations,
- std::vector<MethodReference>& methods)
+ std::vector<ProfileMethodInfo>& methods)
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 54fc0386e1..5638ce1160 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -37,7 +37,7 @@
namespace art {
const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '2', '\0' };
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '3', '\0' }; // inline caches
static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
@@ -46,6 +46,25 @@ static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
// using the same test profile.
static constexpr bool kDebugIgnoreChecksum = false;
+static constexpr uint8_t kMegamorphicEncoding = 7;
+
+static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t),
+ "InlineCache::kIndividualCacheSize does not have the expect type size");
+static_assert(InlineCache::kIndividualCacheSize < kMegamorphicEncoding,
+ "InlineCache::kIndividualCacheSize is larger than expected");
+
+void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx,
+ const dex::TypeIndex& type_idx) {
+ if (is_megamorphic) {
+ return;
+ }
+ classes.emplace(dex_profile_idx, type_idx);
+ if (classes.size() >= InlineCache::kIndividualCacheSize) {
+ is_megamorphic = true;
+ classes.clear();
+ }
+}
+
// Transform the actual dex location into relative paths.
// Note: this is OK because we don't store profiles of different apps into the same file.
// Apps with split apks don't cause trouble because each split has a different name and will not
@@ -62,12 +81,10 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_
}
bool ProfileCompilationInfo::AddMethodsAndClasses(
- const std::vector<MethodReference>& methods,
+ const std::vector<ProfileMethodInfo>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
- for (const MethodReference& method : methods) {
- if (!AddMethodIndex(GetProfileDexFileKey(method.dex_file->GetLocation()),
- method.dex_file->GetLocationChecksum(),
- method.dex_method_index)) {
+ for (const ProfileMethodInfo& method : methods) {
+ if (!AddMethod(method)) {
return false;
}
}
@@ -170,29 +187,40 @@ static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) {
}
static constexpr size_t kLineHeaderSize =
- 3 * sizeof(uint16_t) + // method_set.size + class_set.size + dex_location.size
- sizeof(uint32_t); // checksum
+ 2 * sizeof(uint16_t) + // class_set.size + dex_location.size
+ 2 * sizeof(uint32_t); // method_map.size + checksum
/**
* Serialization format:
- * magic,version,number_of_lines
- * dex_location1,number_of_methods1,number_of_classes1,dex_location_checksum1, \
- * method_id11,method_id12...,class_id1,class_id2...
- * dex_location2,number_of_methods2,number_of_classes2,dex_location_checksum2, \
- * method_id21,method_id22...,,class_id1,class_id2...
+ * magic,version,number_of_dex_files
+ * dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \
+ * method_encoding_11,method_encoding_12...,class_id1,class_id2...
+ * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \
+ * method_encoding_21,method_encoding_22...,,class_id1,class_id2...
* .....
+ * The method_encoding is:
+ * method_id,number_of_inline_caches,inline_cache1,inline_cache2...
+ * The inline_cache is:
+ * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,...
+ * dex_map_size is the number of dex_indeces that follows.
+ * Classes are grouped per their dex files and the line
+ * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the
+ * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...`
+ * M stands for megamorphic and it's encoded as the byte kMegamorphicEncoding.
+ * When present, there will be no class ids following.
**/
bool ProfileCompilationInfo::Save(int fd) {
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
- // Cache at most 5KB before writing.
- static constexpr size_t kMaxSizeToKeepBeforeWriting = 5 * KB;
+ // Cache at most 50KB before writing.
+ static constexpr size_t kMaxSizeToKeepBeforeWriting = 50 * KB;
// Use a vector wrapper to avoid keeping track of offsets when we add elements.
std::vector<uint8_t> buffer;
WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
- AddUintToBuffer(&buffer, static_cast<uint16_t>(info_.size()));
+ DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max());
+ AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size()));
for (const auto& it : info_) {
if (buffer.size() > kMaxSizeToKeepBeforeWriting) {
@@ -203,9 +231,9 @@ bool ProfileCompilationInfo::Save(int fd) {
}
const std::string& dex_location = it.first;
const DexFileData& dex_data = it.second;
- if (dex_data.method_set.empty() && dex_data.class_set.empty()) {
- continue;
- }
+
+ // Note that we allow dex files without any methods or classes, so that
+ // inline caches can refer valid dex files.
if (dex_location.size() >= kMaxDexFileKeyLength) {
LOG(WARNING) << "DexFileKey exceeds allocated limit";
@@ -214,42 +242,128 @@ bool ProfileCompilationInfo::Save(int fd) {
// Make sure that the buffer has enough capacity to avoid repeated resizings
// while we add data.
+ uint32_t methods_region_size = GetMethodsRegionSize(dex_data);
size_t required_capacity = buffer.size() +
kLineHeaderSize +
dex_location.size() +
- sizeof(uint16_t) * (dex_data.class_set.size() + dex_data.method_set.size());
+ sizeof(uint16_t) * dex_data.class_set.size() +
+ methods_region_size;
buffer.reserve(required_capacity);
-
DCHECK_LE(dex_location.size(), std::numeric_limits<uint16_t>::max());
- DCHECK_LE(dex_data.method_set.size(), std::numeric_limits<uint16_t>::max());
DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max());
AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_location.size()));
- AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.method_set.size()));
AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size()));
+ AddUintToBuffer(&buffer, methods_region_size); // uint32_t
AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t
AddStringToBuffer(&buffer, dex_location);
- for (auto method_it : dex_data.method_set) {
- AddUintToBuffer(&buffer, method_it);
+ for (const auto& method_it : dex_data.method_map) {
+ AddUintToBuffer(&buffer, method_it.first);
+ AddInlineCacheToBuffer(&buffer, method_it.second);
}
- for (auto class_id : dex_data.class_set) {
+ for (const auto& class_id : dex_data.class_set) {
AddUintToBuffer(&buffer, class_id.index_);
}
- DCHECK_EQ(required_capacity, buffer.size())
+
+ DCHECK_LE(required_capacity, buffer.size())
<< "Failed to add the expected number of bytes in the buffer";
}
return WriteBuffer(fd, buffer.data(), buffer.size());
}
+void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
+ const InlineCacheMap& inline_cache_map) {
+ // Add inline cache map size.
+ AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size()));
+ if (inline_cache_map.size() == 0) {
+ return;
+ }
+ for (const auto& inline_cache_it : inline_cache_map) {
+ uint16_t dex_pc = inline_cache_it.first;
+ const DexPcData dex_pc_data = inline_cache_it.second;
+ const ClassSet& classes = dex_pc_data.classes;
+
+ // Add the dex pc.
+ AddUintToBuffer(buffer, dex_pc);
+
+ if (dex_pc_data.is_megamorphic) {
+ // Add the megamorphic encoding if needed and continue.
+ // If megamorphic, we don't add the rest of the classes.
+ AddUintToBuffer(buffer, kMegamorphicEncoding);
+ continue;
+ }
+
+ DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize);
+ DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";
+
+ SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
+ // Group the classes by dex. We expect that most of the classes will come from
+ // the same dex, so this will be more efficient than encoding the dex index
+ // for each class reference.
+ GroupClassesByDex(classes, &dex_to_classes_map);
+ // Add the dex map size.
+ AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size()));
+ for (const auto& dex_it : dex_to_classes_map) {
+ uint8_t dex_profile_index = dex_it.first;
+ const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
+ // Add the dex profile index.
+ AddUintToBuffer(buffer, dex_profile_index);
+ // Add the the number of classes for each dex profile index.
+ AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size()));
+ for (size_t i = 0; i < dex_classes.size(); i++) {
+ // Add the type index of the classes.
+ AddUintToBuffer(buffer, dex_classes[i].index_);
+ }
+ }
+ }
+}
+
+uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) {
+ // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods
+ uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size();
+ for (const auto& method_it : dex_data.method_map) {
+ const InlineCacheMap& inline_cache = method_it.second;
+ size += sizeof(uint16_t) * inline_cache.size(); // dex_pc
+ for (const auto& inline_cache_it : inline_cache) {
+ const ClassSet& classes = inline_cache_it.second.classes;
+ SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map;
+ GroupClassesByDex(classes, &dex_to_classes_map);
+ size += sizeof(uint8_t); // dex_to_classes_map size
+ for (const auto& dex_it : dex_to_classes_map) {
+ size += sizeof(uint8_t); // dex profile index
+ size += sizeof(uint8_t); // number of classes
+ const std::vector<dex::TypeIndex>& dex_classes = dex_it.second;
+ size += sizeof(uint16_t) * dex_classes.size(); // the actual classes
+ }
+ }
+ }
+ return size;
+}
+
+void ProfileCompilationInfo::GroupClassesByDex(
+ const ClassSet& classes,
+ /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) {
+ for (const auto& classes_it : classes) {
+ auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index);
+ dex_it->second.push_back(classes_it.type_index);
+ }
+}
+
ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData(
const std::string& dex_location,
uint32_t checksum) {
- auto info_it = info_.find(dex_location);
- if (info_it == info_.end()) {
- info_it = info_.Put(dex_location, DexFileData(checksum));
+ auto info_it = info_.FindOrAdd(dex_location, DexFileData(checksum, info_.size()));
+ if (info_.size() > std::numeric_limits<uint8_t>::max()) {
+ // Allow only 255 dex files to be profiled. This allows us to save bytes
+ // when encoding. The number is well above what we expect for normal applications.
+ if (kIsDebugBuild) {
+ LOG(WARNING) << "Exceeded the maximum number of dex files (255). Something went wrong";
+ }
+ info_.erase(dex_location);
+ return nullptr;
}
if (info_it->second.checksum != checksum) {
LOG(WARNING) << "Checksum mismatch for dex " << dex_location;
@@ -270,13 +384,65 @@ bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& c
}
bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location,
- uint32_t checksum,
- uint16_t method_idx) {
- DexFileData* const data = GetOrAddDexFileData(dex_location, checksum);
- if (data == nullptr) {
+ uint32_t dex_checksum,
+ uint16_t method_index) {
+ return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo());
+}
+
+bool ProfileCompilationInfo::AddMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t method_index,
+ const OfflineProfileMethodInfo& pmi) {
+ DexFileData* const data = GetOrAddDexFileData(
+ GetProfileDexFileKey(dex_location),
+ dex_checksum);
+ if (data == nullptr) { // checksum mismatch
return false;
}
- data->method_set.insert(method_idx);
+ auto inline_cache_it = data->method_map.FindOrAdd(method_index);
+ for (const auto& pmi_inline_cache_it : pmi.inline_caches) {
+ uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first;
+ const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second;
+ auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc);
+ if (pmi_ic_dex_pc_data.is_megamorphic) {
+ dex_pc_data_it->second.SetMegamorphic();
+ continue;
+ }
+ for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) {
+ const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index];
+ DexFileData* class_dex_data = GetOrAddDexFileData(
+ GetProfileDexFileKey(dex_ref.dex_location),
+ dex_ref.dex_checksum);
+ if (class_dex_data == nullptr) { // checksum mismatch
+ return false;
+ }
+ dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
+ }
+ }
+ return true;
+}
+
+bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) {
+ DexFileData* const data = GetOrAddDexFileData(
+ GetProfileDexFileKey(pmi.dex_file->GetLocation()),
+ pmi.dex_file->GetLocationChecksum());
+ if (data == nullptr) { // checksum mismatch
+ return false;
+ }
+ auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index);
+
+ for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) {
+ for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) {
+ DexFileData* class_dex_data = GetOrAddDexFileData(
+ GetProfileDexFileKey(class_ref.dex_file->GetLocation()),
+ class_ref.dex_file->GetLocationChecksum());
+ if (class_dex_data == nullptr) { // checksum mismatch
+ return false;
+ }
+ auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc);
+ dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index);
+ }
+ }
return true;
}
@@ -291,21 +457,79 @@ bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location,
return true;
}
-bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer,
- uint16_t method_set_size,
- uint16_t class_set_size,
- uint32_t checksum,
- const std::string& dex_location) {
- for (uint16_t i = 0; i < method_set_size; i++) {
- uint16_t method_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
- if (!AddMethodIndex(dex_location, checksum, method_idx)) {
+#define READ_UINT(type, buffer, dest, error) \
+ do { \
+ if (!buffer.ReadUintAndAdvance<type>(&dest)) { \
+ *error = "Could not read "#dest; \
+ return false; \
+ } \
+ } \
+ while (false)
+
+bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ /*out*/ InlineCacheMap* inline_cache,
+ /*out*/ std::string* error) {
+ uint16_t inline_cache_size;
+ READ_UINT(uint16_t, buffer, inline_cache_size, error);
+ for (; inline_cache_size > 0; inline_cache_size--) {
+ uint16_t dex_pc;
+ uint8_t dex_to_classes_map_size;
+ READ_UINT(uint16_t, buffer, dex_pc, error);
+ READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error);
+ auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc);
+ if (dex_to_classes_map_size == kMegamorphicEncoding) {
+ dex_pc_data_it->second.SetMegamorphic();
+ continue;
+ }
+ for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) {
+ uint8_t dex_profile_index;
+ uint8_t dex_classes_size;
+ READ_UINT(uint8_t, buffer, dex_profile_index, error);
+ READ_UINT(uint8_t, buffer, dex_classes_size, error);
+ if (dex_profile_index >= number_of_dex_files) {
+ *error = "dex_profile_index out of bounds ";
+ *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files);
+ return false;
+ }
+ for (; dex_classes_size > 0; dex_classes_size--) {
+ uint16_t type_index;
+ READ_UINT(uint16_t, buffer, type_index, error);
+ dex_pc_data_it->second.AddClass(dex_profile_index, dex::TypeIndex(type_index));
+ }
+ }
+ }
+ return true;
+}
+
+bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error) {
+ while (buffer.HasMoreData()) {
+ DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum);
+ uint16_t method_index;
+ READ_UINT(uint16_t, buffer, method_index, error);
+
+ auto it = data->method_map.FindOrAdd(method_index);
+ if (!ReadInlineCache(buffer, number_of_dex_files, &(it->second), error)) {
return false;
}
}
- for (uint16_t i = 0; i < class_set_size; i++) {
- uint16_t type_idx = line_buffer.ReadUintAndAdvance<uint16_t>();
- if (!AddClassIndex(dex_location, checksum, dex::TypeIndex(type_idx))) {
+ return true;
+}
+
+bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer,
+ uint16_t classes_to_read,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error) {
+ for (uint16_t i = 0; i < classes_to_read; i++) {
+ uint16_t type_index;
+ READ_UINT(uint16_t, buffer, type_index, error);
+ if (!AddClassIndex(line_header.dex_location,
+ line_header.checksum,
+ dex::TypeIndex(type_index))) {
return false;
}
}
@@ -324,15 +548,17 @@ static int testEOF(int fd) {
// Reads an uint value previously written with AddUintToBuffer.
template <typename T>
-T ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance() {
+bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) {
static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
- CHECK_LE(ptr_current_ + sizeof(T), ptr_end_);
- T value = 0;
+ if (ptr_current_ + sizeof(T) > ptr_end_) {
+ return false;
+ }
+ *value = 0;
for (size_t i = 0; i < sizeof(T); i++) {
- value += ptr_current_[i] << (i * kBitsPerByte);
+ *value += ptr_current_[i] << (i * kBitsPerByte);
}
ptr_current_ += sizeof(T);
- return value;
+ return true;
}
bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) {
@@ -346,6 +572,10 @@ bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data,
return false;
}
+bool ProfileCompilationInfo::SafeBuffer::HasMoreData() {
+ return ptr_current_ < ptr_end_;
+}
+
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd(
int fd,
const std::string& source,
@@ -369,13 +599,13 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::Fil
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader(
int fd,
- /*out*/uint16_t* number_of_lines,
+ /*out*/uint8_t* number_of_dex_files,
/*out*/std::string* error) {
// Read magic and version
const size_t kMagicVersionSize =
sizeof(kProfileMagic) +
sizeof(kProfileVersion) +
- sizeof(uint16_t); // number of lines
+ sizeof(uint8_t); // number of dex files
SafeBuffer safe_buffer(kMagicVersionSize);
@@ -392,24 +622,38 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHead
*error = "Profile version mismatch";
return kProfileLoadVersionMismatch;
}
- *number_of_lines = safe_buffer.ReadUintAndAdvance<uint16_t>();
+ if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) {
+ *error = "Cannot read the number of dex files";
+ return kProfileLoadBadData;
+ }
return kProfileLoadSuccess;
}
+bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer,
+ /*out*/uint16_t* dex_location_size,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error) {
+ READ_UINT(uint16_t, buffer, *dex_location_size, error);
+ READ_UINT(uint16_t, buffer, line_header->class_set_size, error);
+ READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error);
+ READ_UINT(uint32_t, buffer, line_header->checksum, error);
+ return true;
+}
+
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader(
int fd,
/*out*/ProfileLineHeader* line_header,
/*out*/std::string* error) {
SafeBuffer header_buffer(kLineHeaderSize);
- ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileHeader", error);
+ ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error);
if (status != kProfileLoadSuccess) {
return status;
}
- uint16_t dex_location_size = header_buffer.ReadUintAndAdvance<uint16_t>();
- line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
- line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint16_t>();
- line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>();
+ uint16_t dex_location_size;
+ if (!ReadProfileLineHeaderElements(header_buffer, &dex_location_size, line_header, error)) {
+ return kProfileLoadBadData;
+ }
if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) {
*error = "DexFileKey has an invalid size: " +
@@ -429,37 +673,38 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine
ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine(
int fd,
+ uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error) {
- // Make sure that we don't try to read everything in memory (in case the profile if full).
- // Split readings in chunks of at most 10kb.
- static constexpr uint16_t kMaxNumberOfEntriesToRead = 5120;
- uint16_t methods_left_to_read = line_header.method_set_size;
- uint16_t classes_left_to_read = line_header.class_set_size;
+ if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) {
+ *error = "Error when reading profile file line header: checksum mismatch for "
+ + line_header.dex_location;
+ return kProfileLoadBadData;
+ }
- while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) {
- uint16_t methods_to_read = std::min(kMaxNumberOfEntriesToRead, methods_left_to_read);
- uint16_t max_classes_to_read = kMaxNumberOfEntriesToRead - methods_to_read;
- uint16_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read);
+ {
+ SafeBuffer buffer(line_header.method_region_size_bytes);
+ ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineMethods", error);
+ if (status != kProfileLoadSuccess) {
+ return status;
+ }
- size_t line_size = sizeof(uint16_t) * (methods_to_read + classes_to_read);
- SafeBuffer line_buffer(line_size);
+ if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) {
+ return kProfileLoadBadData;
+ }
+ }
- ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error);
+ {
+ SafeBuffer buffer(sizeof(uint16_t) * line_header.class_set_size);
+ ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineClasses", error);
if (status != kProfileLoadSuccess) {
return status;
}
- if (!ProcessLine(line_buffer,
- methods_to_read,
- classes_to_read,
- line_header.checksum,
- line_header.dex_location)) {
- *error = "Error when reading profile file line";
+ if (!ReadClasses(buffer, line_header.class_set_size, line_header, error)) {
return kProfileLoadBadData;
}
- methods_left_to_read -= methods_to_read;
- classes_left_to_read -= classes_to_read;
}
+
return kProfileLoadSuccess;
}
@@ -470,7 +715,7 @@ bool ProfileCompilationInfo::Load(int fd) {
if (status == kProfileLoadSuccess) {
return true;
} else {
- PLOG(WARNING) << "Error when reading profile " << error;
+ LOG(WARNING) << "Error when reading profile: " << error;
return false;
}
}
@@ -490,15 +735,16 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
if (stat_buffer.st_size == 0) {
return kProfileLoadSuccess;
}
- // Read profile header: magic + version + number_of_lines.
- uint16_t number_of_lines;
- ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error);
+ // Read profile header: magic + version + number_of_dex_files.
+ uint8_t number_of_dex_files;
+ ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_dex_files, error);
if (status != kProfileLoadSuccess) {
return status;
}
- while (number_of_lines > 0) {
+ for (uint8_t k = 0; k < number_of_dex_files; k++) {
ProfileLineHeader line_header;
+
// First, read the line header to get the amount of data we need to read.
status = ReadProfileLineHeader(fd, &line_header, error);
if (status != kProfileLoadSuccess) {
@@ -506,11 +752,10 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal(
}
// Now read the actual profile line.
- status = ReadProfileLine(fd, line_header, error);
+ status = ReadProfileLine(fd, number_of_dex_files, line_header, error);
if (status != kProfileLoadSuccess) {
return status;
}
- number_of_lines--;
}
// Check that we read everything and that profiles don't contain junk data.
@@ -538,37 +783,112 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) {
}
}
// All checksums match. Import the data.
+
+ // The other profile might have a different indexing of dex files.
+ // That is because each dex files gets a 'dex_profile_index' on a first come first served basis.
+ // That means that the order in with the methods are added to the profile matters for the
+ // actual indices.
+ // The reason we cannot rely on the actual multidex index is that a single profile may store
+ // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
+ // and one from split-B.
+
+ // First, build a mapping from other_dex_profile_index to this_dex_profile_index.
+ // This will make sure that the ClassReferences will point to the correct dex file.
+ SafeMap<uint8_t, uint8_t> dex_profile_index_remap;
+ for (const auto& other_it : other.info_) {
+ const std::string& other_dex_location = other_it.first;
+ const DexFileData& other_dex_data = other_it.second;
+ auto info_it = info_.FindOrAdd(other_dex_location, DexFileData(other_dex_data.checksum, 0));
+ const DexFileData& dex_data = info_it->second;
+ dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data.profile_index);
+ }
+
+ // Merge the actual profile data.
for (const auto& other_it : other.info_) {
const std::string& other_dex_location = other_it.first;
const DexFileData& other_dex_data = other_it.second;
auto info_it = info_.find(other_dex_location);
- if (info_it == info_.end()) {
- info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum));
- }
- info_it->second.method_set.insert(other_dex_data.method_set.begin(),
- other_dex_data.method_set.end());
+ DCHECK(info_it != info_.end());
+
+ // Merge the classes.
info_it->second.class_set.insert(other_dex_data.class_set.begin(),
other_dex_data.class_set.end());
+
+ // Merge the methods and the inline caches.
+ for (const auto& other_method_it : other_dex_data.method_map) {
+ uint16_t other_method_index = other_method_it.first;
+ auto method_it = info_it->second.method_map.FindOrAdd(other_method_index);
+ const auto& other_inline_cache = other_method_it.second;
+ for (const auto& other_ic_it : other_inline_cache) {
+ uint16_t other_dex_pc = other_ic_it.first;
+ const ClassSet& other_class_set = other_ic_it.second.classes;
+ auto class_set = method_it->second.FindOrAdd(other_dex_pc);
+ for (const auto& class_it : other_class_set) {
+ class_set->second.AddClass(dex_profile_index_remap.Get(
+ class_it.dex_profile_index), class_it.type_index);
+ }
+ }
+ }
}
return true;
}
+static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) {
+ return kDebugIgnoreChecksum || dex_file_checksum == checksum;
+}
+
static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) {
- return kDebugIgnoreChecksum || dex_file.GetLocationChecksum() == checksum;
+ return ChecksumMatch(dex_file.GetLocationChecksum(), checksum);
}
bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const {
- auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation()));
+ return FindMethod(method_ref.dex_file->GetLocation(),
+ method_ref.dex_file->GetLocationChecksum(),
+ method_ref.dex_method_index) != nullptr;
+}
+
+const ProfileCompilationInfo::InlineCacheMap*
+ProfileCompilationInfo::FindMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index) const {
+ auto info_it = info_.find(GetProfileDexFileKey(dex_location));
if (info_it != info_.end()) {
- if (!ChecksumMatch(*method_ref.dex_file, info_it->second.checksum)) {
- return false;
+ if (!ChecksumMatch(dex_checksum, info_it->second.checksum)) {
+ return nullptr;
}
- const std::set<uint16_t>& methods = info_it->second.method_set;
- return methods.find(method_ref.dex_method_index) != methods.end();
+ const MethodMap& methods = info_it->second.method_map;
+ const auto method_it = methods.find(dex_method_index);
+ return method_it == methods.end() ? nullptr : &(method_it->second);
}
- return false;
+ return nullptr;
}
+void ProfileCompilationInfo::DexFileToProfileIndex(
+ /*out*/std::vector<DexReference>* dex_references) const {
+ dex_references->resize(info_.size());
+ for (const auto& info_it : info_) {
+ DexReference& dex_ref = (*dex_references)[info_it.second.profile_index];
+ dex_ref.dex_location = info_it.first;
+ dex_ref.dex_checksum = info_it.second.checksum;
+ }
+}
+
+bool ProfileCompilationInfo::GetMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index,
+ /*out*/OfflineProfileMethodInfo* pmi) const {
+ const InlineCacheMap* inline_caches = FindMethod(dex_location, dex_checksum, dex_method_index);
+ if (inline_caches == nullptr) {
+ return false;
+ }
+
+ DexFileToProfileIndex(&pmi->dex_references);
+ // TODO(calin): maybe expose a direct pointer to avoid copying
+ pmi->inline_caches = *inline_caches;
+ return true;
+}
+
+
bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const {
auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation()));
if (info_it != info_.end()) {
@@ -584,7 +904,7 @@ bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeInd
uint32_t ProfileCompilationInfo::GetNumberOfMethods() const {
uint32_t total = 0;
for (const auto& it : info_) {
- total += it.second.method_set.size();
+ total += it.second.method_map.size();
}
return total;
}
@@ -645,19 +965,34 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>*
}
}
os << "\n\tmethods: ";
- for (const auto method_it : dex_data.method_set) {
+ for (const auto method_it : dex_data.method_map) {
if (dex_file != nullptr) {
- os << "\n\t\t" << dex_file->PrettyMethod(method_it, true);
+ os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true);
} else {
- os << method_it << ",";
+ os << method_it.first;
+ }
+
+ os << "[";
+ for (const auto& inline_cache_it : method_it.second) {
+ os << "{" << std::hex << inline_cache_it.first << std::dec << ":";
+ if (inline_cache_it.second.is_megamorphic) {
+ os << "M";
+ } else {
+ for (const ClassReference& class_ref : inline_cache_it.second.classes) {
+ os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index)
+ << "," << class_ref.type_index.index_ << ")";
+ }
+ }
+ os << "}";
}
+ os << "], ";
}
os << "\n\tclasses: ";
for (const auto class_it : dex_data.class_set) {
if (dex_file != nullptr) {
os << "\n\t\t" << dex_file->PrettyType(class_it);
} else {
- os << class_it << ",";
+ os << class_it.index_ << ",";
}
}
}
@@ -762,4 +1097,44 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd,
return info.Save(fd);
}
+bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==(
+ const OfflineProfileMethodInfo& other) const {
+ if (inline_caches.size() != other.inline_caches.size()) {
+ return false;
+ }
+
+ // We can't use a simple equality test because we need to match the dex files
+ // of the inline caches which might have different profile indices.
+ for (const auto& inline_cache_it : inline_caches) {
+ uint16_t dex_pc = inline_cache_it.first;
+ const DexPcData dex_pc_data = inline_cache_it.second;
+ const auto other_it = other.inline_caches.find(dex_pc);
+ if (other_it == other.inline_caches.end()) {
+ return false;
+ }
+ const DexPcData& other_dex_pc_data = other_it->second;
+ if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic) {
+ return false;
+ }
+ for (const ClassReference& class_ref : dex_pc_data.classes) {
+ bool found = false;
+ for (const ClassReference& other_class_ref : other_dex_pc_data.classes) {
+ CHECK_LE(class_ref.dex_profile_index, dex_references.size());
+ CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size());
+ const DexReference& dex_ref = dex_references[class_ref.dex_profile_index];
+ const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index];
+ if (class_ref.type_index == other_class_ref.type_index &&
+ dex_ref == other_dex_ref) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 758b46d74a..4bfbfcd287 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -31,6 +31,40 @@
namespace art {
/**
+ * Convenient class to pass around profile information (including inline caches)
+ * without the need to hold GC-able objects.
+ */
+struct ProfileMethodInfo {
+ struct ProfileClassReference {
+ ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index)
+ : dex_file(dex), type_index(index) {}
+
+ const DexFile* dex_file;
+ const dex::TypeIndex type_index;
+ };
+
+ struct ProfileInlineCache {
+ ProfileInlineCache(uint32_t pc, const std::vector<ProfileClassReference>& profile_classes)
+ : dex_pc(pc), classes(profile_classes) {}
+
+ const uint32_t dex_pc;
+ const std::vector<ProfileClassReference> classes;
+ };
+
+ ProfileMethodInfo(const DexFile* dex, uint32_t method_index)
+ : dex_file(dex), dex_method_index(method_index) {}
+
+ ProfileMethodInfo(const DexFile* dex,
+ uint32_t method_index,
+ const std::vector<ProfileInlineCache>& caches)
+ : dex_file(dex), dex_method_index(method_index), inline_caches(caches) {}
+
+ const DexFile* dex_file;
+ const uint32_t dex_method_index;
+ const std::vector<ProfileInlineCache> inline_caches;
+};
+
+/**
* Profile information in a format suitable to be queried by the compiler and
* performing profile guided compilation.
* It is a serialize-friendly format based on information collected by the
@@ -42,34 +76,130 @@ class ProfileCompilationInfo {
static const uint8_t kProfileMagic[];
static const uint8_t kProfileVersion[];
+ // Data structures for encoding the offline representation of inline caches.
+ // This is exposed as public in order to make it available to dex2oat compilations
+ // (see compiler/optimizing/inliner.cc).
+
+ // A dex location together with its checksum.
+ struct DexReference {
+ DexReference() {}
+
+ DexReference(const std::string& location, uint32_t checksum)
+ : dex_location(location), dex_checksum(checksum) {}
+
+ bool operator==(const DexReference& other) const {
+ return dex_checksum == other.dex_checksum && dex_location == other.dex_location;
+ }
+
+ std::string dex_location;
+ uint32_t dex_checksum;
+ };
+
+ // Encodes a class reference in the profile.
+ // The owning dex file is encoded as the index (dex_profile_index) it has in the
+ // profile rather than as a full DexRefence(location,checksum).
+ // This avoids excessive string copying when managing the profile data.
+ // The dex_profile_index is an index in either of:
+ // - OfflineProfileMethodInfo#dex_references vector (public use)
+ // - DexFileData#profile_index (internal use).
+ // Note that the dex_profile_index is not necessary the multidex index.
+ // We cannot rely on the actual multidex index because a single profile may store
+ // data from multiple splits. This means that a profile may contain a classes2.dex from split-A
+ // and one from split-B.
+ struct ClassReference {
+ ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex& type_idx) :
+ dex_profile_index(dex_profile_idx), type_index(type_idx) {}
+
+ bool operator==(const ClassReference& other) const {
+ return dex_profile_index == other.dex_profile_index && type_index == other.type_index;
+ }
+ bool operator<(const ClassReference& other) const {
+ return dex_profile_index == other.dex_profile_index
+ ? type_index < other.type_index
+ : dex_profile_index < other.dex_profile_index;
+ }
+
+ uint8_t dex_profile_index; // the index of the owning dex in the profile info
+ dex::TypeIndex type_index; // the type index of the class
+ };
+
+ // The set of classes that can be found at a given dex pc.
+ using ClassSet = std::set<ClassReference>;
+
+ // Encodes the actual inline cache for a given dex pc (whether or not the receiver is
+ // megamorphic and its possible types).
+ // If the receiver is megamorphic the set of classes will be empty.
+ struct DexPcData {
+ DexPcData() : is_megamorphic(false) {}
+ void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx);
+ void SetMegamorphic() {
+ is_megamorphic = true;
+ classes.clear();
+ }
+ bool operator==(const DexPcData& other) const {
+ return is_megamorphic == other.is_megamorphic && classes == other.classes;
+ }
+
+ bool is_megamorphic;
+ ClassSet classes;
+ };
+
+ // The inline cache map: DexPc -> DexPcData.
+ using InlineCacheMap = SafeMap<uint16_t, DexPcData>;
+
+ // Encodes the full set of inline caches for a given method.
+ // The dex_references vector is indexed according to the ClassReference::dex_profile_index.
+ // i.e. the dex file of any ClassReference present in the inline caches can be found at
+ // dex_references[ClassReference::dex_profile_index].
+ struct OfflineProfileMethodInfo {
+ bool operator==(const OfflineProfileMethodInfo& other) const;
+
+ std::vector<DexReference> dex_references;
+ InlineCacheMap inline_caches;
+ };
+
+ // Public methods to create, extend or query the profile.
+
// Add the given methods and classes to the current profile object.
- bool AddMethodsAndClasses(const std::vector<MethodReference>& methods,
+ bool AddMethodsAndClasses(const std::vector<ProfileMethodInfo>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes);
- // Loads profile information from the given file descriptor.
+
+ // Load profile information from the given file descriptor.
bool Load(int fd);
+
// Merge the data from another ProfileCompilationInfo into the current object.
bool MergeWith(const ProfileCompilationInfo& info);
- // Saves the profile data to the given file descriptor.
+
+ // Save the profile data to the given file descriptor.
bool Save(int fd);
- // Loads and merges profile information from the given file into the current
+
+ // Load and merge profile information from the given file into the current
// object and tries to save it back to disk.
// If `force` is true then the save will go through even if the given file
// has bad data or its version does not match. In this cases the profile content
// is ignored.
bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force);
- // Returns the number of methods that were profiled.
+ // Return the number of methods that were profiled.
uint32_t GetNumberOfMethods() const;
- // Returns the number of resolved classes that were profiled.
+
+ // Return the number of resolved classes that were profiled.
uint32_t GetNumberOfResolvedClasses() const;
- // Returns true if the method reference is present in the profiling info.
+ // Return true if the method reference is present in the profiling info.
bool ContainsMethod(const MethodReference& method_ref) const;
- // Returns true if the class's type is present in the profiling info.
+ // Return true if the class's type is present in the profiling info.
bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const;
- // Dumps all the loaded profile info into a string and returns it.
+ // Return true if the method is present in the profiling info.
+ // If the method is found, `pmi` is populated with its inline caches.
+ bool GetMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index,
+ /*out*/OfflineProfileMethodInfo* pmi) const;
+
+ // Dump all the loaded profile info into a string and returns it.
// If dex_files is not null then the method indices will be resolved to their
// names.
// This is intended for testing and debugging.
@@ -80,26 +210,35 @@ class ProfileCompilationInfo {
void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::set<std::string>* class_names) const;
+
void GetClassNames(const std::vector<const DexFile*>* dex_files,
std::set<std::string>* class_names) const;
+ // Perform an equality test with the `other` profile information.
bool Equals(const ProfileCompilationInfo& other);
- static std::string GetProfileDexFileKey(const std::string& dex_location);
-
- // Returns the class descriptors for all of the classes in the profiles' class sets.
+ // Return the class descriptors for all of the classes in the profiles' class sets.
// Note the dex location is actually the profile key, the caller needs to call back in to the
// profile info stuff to generate a map back to the dex location.
std::set<DexCacheResolvedClasses> GetResolvedClasses() const;
- // Clears the resolved classes from the current object.
+ // Clear the resolved classes from the current object.
void ClearResolvedClasses();
+ // Return the profile key associated with the given dex location.
+ static std::string GetProfileDexFileKey(const std::string& dex_location);
+
+ // Generate a test profile which will contain a percentage of the total maximum
+ // number of methods and classes (method_ratio and class_ratio).
static bool GenerateTestProfile(int fd,
uint16_t number_of_dex_files,
uint16_t method_ratio,
uint16_t class_ratio);
+ // Check that the given profile method info contain the same data.
+ static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1,
+ const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi2);
+
private:
enum ProfileLoadSatus {
kProfileLoadIOError,
@@ -108,30 +247,71 @@ class ProfileCompilationInfo {
kProfileLoadSuccess
};
+ // Maps a method dex index to its inline cache.
+ using MethodMap = SafeMap<uint16_t, InlineCacheMap>;
+
+ // Internal representation of the profile information belonging to a dex file.
struct DexFileData {
- explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {}
+ DexFileData(uint32_t location_checksum, uint16_t index)
+ : profile_index(index), checksum(location_checksum) {}
+ // The profile index of this dex file (matches ClassReference#dex_profile_index)
+ uint8_t profile_index;
+ // The dex checksum
uint32_t checksum;
- std::set<uint16_t> method_set;
+ // The methonds' profile information
+ MethodMap method_map;
+ // The classes which have been profiled. Note that these don't necessarily include
+ // all the classes that can be found in the inline caches reference.
std::set<dex::TypeIndex> class_set;
bool operator==(const DexFileData& other) const {
- return checksum == other.checksum && method_set == other.method_set;
+ return checksum == other.checksum && method_map == other.method_map;
}
};
+ // Maps dex file to their profile information.
using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>;
+ // Return the profile data for the given dex location or null if the dex location
+ // already exists but has a different checksum
DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum);
+
+ // Add a method index to the profile (without inline caches).
bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+
+ // Add a method to the profile using its online representation (containing runtime structures).
+ bool AddMethod(const ProfileMethodInfo& pmi);
+
+ // Add a method to the profile using its offline representation.
+ // This is mostly used to facilitate testing.
+ bool AddMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t method_index,
+ const OfflineProfileMethodInfo& pmi);
+
+ // Add a class index to the profile.
bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx);
+
+ // Add all classes from the given dex cache to the the profile.
bool AddResolvedClasses(const DexCacheResolvedClasses& classes);
+ // Search for the given method in the profile.
+ // If found, its inline cache map is returned, otherwise the method returns null.
+ const InlineCacheMap* FindMethod(const std::string& dex_location,
+ uint32_t dex_checksum,
+ uint16_t dex_method_index) const;
+
+ // Encode the known dex_files into a vector. The index of a dex_reference will
+ // be the same as the profile index of the dex file (used to encode the ClassReferences).
+ void DexFileToProfileIndex(/*out*/std::vector<DexReference>* dex_references) const;
+
// Parsing functionality.
+ // The information present in the header of each profile line.
struct ProfileLineHeader {
std::string dex_location;
- uint16_t method_set_size;
uint16_t class_set_size;
+ uint32_t method_region_size_bytes;
uint32_t checksum;
};
@@ -150,12 +330,15 @@ class ProfileCompilationInfo {
// Reads an uint value (high bits to low bits) and advances the current pointer
// with the number of bits read.
- template <typename T> T ReadUintAndAdvance();
+ template <typename T> bool ReadUintAndAdvance(/*out*/ T* value);
// Compares the given data with the content current pointer. If the contents are
// equal it advances the current pointer by data_size.
bool CompareAndAdvance(const uint8_t* data, size_t data_size);
+ // Returns true if the buffer has more data to read.
+ bool HasMoreData();
+
// Get the underlying raw buffer.
uint8_t* Get() { return storage_.get(); }
@@ -165,24 +348,63 @@ class ProfileCompilationInfo {
uint8_t* ptr_end_;
};
+ // Entry point for profile loding functionality.
ProfileLoadSatus LoadInternal(int fd, std::string* error);
+ // Read the profile header from the given fd and store the number of profile
+ // lines into number_of_dex_files.
ProfileLoadSatus ReadProfileHeader(int fd,
- /*out*/uint16_t* number_of_lines,
+ /*out*/uint8_t* number_of_dex_files,
/*out*/std::string* error);
+ // Read the header of a profile line from the given fd.
ProfileLoadSatus ReadProfileLineHeader(int fd,
/*out*/ProfileLineHeader* line_header,
/*out*/std::string* error);
+
+ // Read individual elements from the profile line header.
+ bool ReadProfileLineHeaderElements(SafeBuffer& buffer,
+ /*out*/uint16_t* dex_location_size,
+ /*out*/ProfileLineHeader* line_header,
+ /*out*/std::string* error);
+
+ // Read a single profile line from the given fd.
ProfileLoadSatus ReadProfileLine(int fd,
+ uint8_t number_of_dex_files,
const ProfileLineHeader& line_header,
/*out*/std::string* error);
- bool ProcessLine(SafeBuffer& line_buffer,
- uint16_t method_set_size,
- uint16_t class_set_size,
- uint32_t checksum,
- const std::string& dex_location);
+ // Read all the classes from the buffer into the profile `info_` structure.
+ bool ReadClasses(SafeBuffer& buffer,
+ uint16_t classes_to_read,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error);
+
+ // Read all the methods from the buffer into the profile `info_` structure.
+ bool ReadMethods(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ const ProfileLineHeader& line_header,
+ /*out*/std::string* error);
+
+ // Read the inline cache encoding from line_bufer into inline_cache.
+ bool ReadInlineCache(SafeBuffer& buffer,
+ uint8_t number_of_dex_files,
+ /*out*/InlineCacheMap* inline_cache,
+ /*out*/std::string* error);
+
+ // Encode the inline cache into the given buffer.
+ void AddInlineCacheToBuffer(std::vector<uint8_t>* buffer,
+ const InlineCacheMap& inline_cache);
+
+ // Return the number of bytes needed to encode the profile information
+ // for the methods in dex_data.
+ uint32_t GetMethodsRegionSize(const DexFileData& dex_data);
+
+ // Group `classes` by their owning dex profile index and put the result in
+ // `dex_to_classes_map`.
+ void GroupClassesByDex(
+ const ClassSet& classes,
+ /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map);
friend class ProfileCompilationInfoTest;
friend class CompilerDriverProfileTest;
diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc
index 835a5f3495..93b47acf46 100644
--- a/runtime/jit/profile_compilation_info_test.cc
+++ b/runtime/jit/profile_compilation_info_test.cc
@@ -57,6 +57,14 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
return info->AddMethodIndex(dex_location, checksum, method_index);
}
+ bool AddMethod(const std::string& dex_location,
+ uint32_t checksum,
+ uint16_t method_index,
+ const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
+ ProfileCompilationInfo* info) {
+ return info->AddMethod(dex_location, checksum, method_index, pmi);
+ }
+
bool AddClass(const std::string& dex_location,
uint32_t checksum,
uint16_t class_index,
@@ -73,17 +81,132 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest {
const std::vector<ArtMethod*>& methods,
const std::set<DexCacheResolvedClasses>& resolved_classes) {
ProfileCompilationInfo info;
- std::vector<MethodReference> method_refs;
+ std::vector<ProfileMethodInfo> profile_methods;
+ ScopedObjectAccess soa(Thread::Current());
+ for (ArtMethod* method : methods) {
+ profile_methods.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
+ }
+ if (!info.AddMethodsAndClasses(profile_methods, resolved_classes)) {
+ return false;
+ }
+ if (info.GetNumberOfMethods() != profile_methods.size()) {
+ return false;
+ }
+ return info.MergeAndSave(filename, nullptr, false);
+ }
+
+ // Saves the given art methods to a profile backed by 'filename' and adds
+ // some fake inline caches to it. The added inline caches are returned in
+ // the out map `profile_methods_map`.
+ bool SaveProfilingInfoWithFakeInlineCaches(
+ const std::string& filename,
+ const std::vector<ArtMethod*>& methods,
+ /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
+ ProfileCompilationInfo info;
+ std::vector<ProfileMethodInfo> profile_methods;
ScopedObjectAccess soa(Thread::Current());
for (ArtMethod* method : methods) {
- method_refs.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
+ std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
+ // Monomorphic
+ for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
+ caches.emplace_back(dex_pc, classes);
+ }
+ // Polymorphic
+ for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
+ classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
+ }
+ caches.emplace_back(dex_pc, classes);
+ }
+ // Megamorphic
+ for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) {
+ std::vector<ProfileMethodInfo::ProfileClassReference> classes;
+ for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
+ classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
+ }
+ caches.emplace_back(dex_pc, classes);
+ }
+ ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches);
+ profile_methods.push_back(pmi);
+ profile_methods_map->Put(method, pmi);
+ }
+
+ if (!info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>())) {
+ return false;
}
- if (!info.AddMethodsAndClasses(method_refs, resolved_classes)) {
+ if (info.GetNumberOfMethods() != profile_methods.size()) {
return false;
}
return info.MergeAndSave(filename, nullptr, false);
}
+ ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
+ const ProfileMethodInfo& pmi) {
+ ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
+ SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
+ for (const auto& inline_cache : pmi.inline_caches) {
+ for (const auto& class_ref : inline_cache.classes) {
+ uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
+ static_cast<uint8_t>(dex_map.size()))->second;
+ offline_pmi.inline_caches
+ .FindOrAdd(inline_cache.dex_pc)->second
+ .AddClass(dex_profile_index, class_ref.type_index);
+ if (dex_profile_index >= offline_pmi.dex_references.size()) {
+ // This is a new dex.
+ const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
+ class_ref.dex_file->GetLocation());
+ offline_pmi.dex_references.emplace_back(dex_key,
+ class_ref.dex_file->GetLocationChecksum());
+ }
+ }
+ }
+ return offline_pmi;
+ }
+
+ // Creates an offline profile used for testing inline caches.
+ ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
+ pmi.dex_references.emplace_back("dex_location3", /* checksum */ 3);
+
+ // Monomorphic
+ for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+ // Polymorphic
+ for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ dex_pc_data.AddClass(1, dex::TypeIndex(1));
+ dex_pc_data.AddClass(2, dex::TypeIndex(2));
+
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+ // Megamorphic
+ for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.is_megamorphic = true;
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+
+ return pmi;
+ }
+
+ void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
+ for (auto it : pmi->inline_caches) {
+ for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
+ it.second.AddClass(0, dex::TypeIndex(k));
+ }
+ }
+ }
+
// Cannot sizeof the actual arrays so hardcode the values here.
// They should not change anyway.
static constexpr int kProfileMagicSize = 4;
@@ -235,12 +358,12 @@ TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
ScratchFile profile;
- ProfileCompilationInfo empyt_info;
+ ProfileCompilationInfo empty_info;
ProfileCompilationInfo loaded_info;
ASSERT_TRUE(profile.GetFile()->ResetOffset());
ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
- ASSERT_TRUE(loaded_info.Equals(empyt_info));
+ ASSERT_TRUE(loaded_info.Equals(empty_info));
}
TEST_F(ProfileCompilationInfoTest, BadMagic) {
@@ -324,4 +447,214 @@ TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
}
+TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ // Add a method which is part of the same dex file as one of the
+ // class from the inline caches.
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ // Add a method which is outside the set of dex files.
+ ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ 3,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi);
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location4",
+ /* checksum */ 4,
+ /* method_idx */ 3,
+ &loaded_pmi2));
+ ASSERT_TRUE(loaded_pmi2 == pmi);
+}
+
+TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo saved_info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
+
+ // Add methods with inline caches.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
+ }
+
+ ASSERT_TRUE(saved_info.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Make the inline caches megamorphic and add them to the profile again.
+ ProfileCompilationInfo saved_info_extra;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
+ MakeMegamorphic(&pmi_extra);
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
+ }
+
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
+ ASSERT_EQ(0, profile.GetFile()->Flush());
+
+ // Merge the profiles so that we have the same view as the file.
+ ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
+
+ // Check that we get back what we saved.
+ ProfileCompilationInfo loaded_info;
+ ASSERT_TRUE(profile.GetFile()->ResetOffset());
+ ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
+
+ ASSERT_TRUE(loaded_info.Equals(saved_info));
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(loaded_info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ 3,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi_extra);
+}
+
+TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
+ ScratchFile profile;
+
+ Thread* self = Thread::Current();
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ }
+ ASSERT_NE(class_loader, nullptr);
+
+ // Save virtual methods from Main.
+ std::set<DexCacheResolvedClasses> resolved_classes;
+ std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
+
+ SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
+ ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
+ profile.GetFilename(), main_methods, &profile_methods_map));
+
+ // Check that what we saved is in the profile.
+ ProfileCompilationInfo info;
+ ASSERT_TRUE(info.Load(GetFd(profile)));
+ ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
+ {
+ ScopedObjectAccess soa(self);
+ for (ArtMethod* m : main_methods) {
+ ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
+ const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
+ ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi;
+ ASSERT_TRUE(info.GetMethod(m->GetDexFile()->GetLocation(),
+ m->GetDexFile()->GetLocationChecksum(),
+ m->GetDexMethodIndex(),
+ &offline_pmi));
+ ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
+ ConvertProfileMethodInfo(pmi);
+ ASSERT_EQ(converted_pmi, offline_pmi);
+ }
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCahce) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo info;
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo();
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo();
+ // Modify the checksum to trigger a mismatch.
+ pmi2.dex_references[0].dex_checksum++;
+
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info));
+ ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info));
+}
+
+// Verify that profiles behave correctly even if the methods are added in a different
+// order and with a different dex profile indices for the dex files.
+TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
+ ScratchFile profile;
+
+ ProfileCompilationInfo info;
+ ProfileCompilationInfo info_reindexed;
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi;
+ pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
+ for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(0, dex::TypeIndex(0));
+ dex_pc_data.AddClass(1, dex::TypeIndex(1));
+ pmi.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+
+ ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed;
+ pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2);
+ pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1);
+ for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
+ ProfileCompilationInfo::DexPcData dex_pc_data;
+ dex_pc_data.AddClass(1, dex::TypeIndex(0));
+ dex_pc_data.AddClass(0, dex::TypeIndex(1));
+ pmi_reindexed.inline_caches.Put(dex_pc, dex_pc_data);
+ }
+
+ // Profile 1 and Profile 2 get the same methods but in different order.
+ // This will trigger a different dex numbers.
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info));
+ ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info));
+ }
+
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ASSERT_TRUE(AddMethod(
+ "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed));
+ ASSERT_TRUE(AddMethod(
+ "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed));
+ }
+
+ ProfileCompilationInfo info_backup = info;
+ ASSERT_TRUE(info.MergeWith(info_reindexed));
+ // Merging should have no effect as we're adding the exact same stuff.
+ ASSERT_TRUE(info.Equals(info_backup));
+ for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1;
+ ASSERT_TRUE(info.GetMethod("dex_location1",
+ /* checksum */ 1,
+ /* method_idx */ method_idx,
+ &loaded_pmi1));
+ ASSERT_TRUE(loaded_pmi1 == pmi);
+ ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2;
+ ASSERT_TRUE(info.GetMethod("dex_location2",
+ /* checksum */ 2,
+ /* method_idx */ method_idx,
+ &loaded_pmi2));
+ ASSERT_TRUE(loaded_pmi2 == pmi);
+ }
+}
+
+TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) {
+ ProfileCompilationInfo info;
+ // Save a few methods.
+ for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) {
+ std::string dex_location = std::to_string(i);
+ ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info));
+ }
+ // We only support at most 255 dex files.
+ ASSERT_FALSE(AddMethod(
+ /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info));
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index 025d10ccc0..61e6c4126a 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -236,10 +236,10 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
std::set<DexCacheResolvedClasses> resolved_classes_for_location;
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
- std::vector<MethodReference> methods_for_location;
+ std::vector<ProfileMethodInfo> profile_methods_for_location;
for (const MethodReference& ref : methods) {
if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) {
- methods_for_location.push_back(ref);
+ profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index);
}
}
for (const DexCacheResolvedClasses& classes : resolved_classes) {
@@ -253,7 +253,7 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
}
}
ProfileCompilationInfo* info = GetCachedProfiledInfo(filename);
- info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location);
+ info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location);
total_number_of_profile_entries_cached += resolved_classes_for_location.size();
}
max_number_of_profile_entries_cached_ = std::max(
@@ -280,15 +280,15 @@ bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) {
}
const std::string& filename = it.first;
const std::set<std::string>& locations = it.second;
- std::vector<MethodReference> methods;
+ std::vector<ProfileMethodInfo> profile_methods;
{
ScopedObjectAccess soa(Thread::Current());
- jit_code_cache_->GetProfiledMethods(locations, methods);
+ jit_code_cache_->GetProfiledMethods(locations, profile_methods);
total_number_of_code_cache_queries_++;
}
ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename);
- cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>());
+ cached_info->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>());
int64_t delta_number_of_methods =
cached_info->GetNumberOfMethods() -
static_cast<int64_t>(last_save_number_of_methods_);
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index 1c58a83679..f42a8da8fa 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -39,7 +39,7 @@ class Class;
// Once the classes_ array is full, we consider the INVOKE to be megamorphic.
class InlineCache {
public:
- static constexpr uint16_t kIndividualCacheSize = 5;
+ static constexpr uint8_t kIndividualCacheSize = 5;
private:
uint32_t dex_pc_;
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 6ecfd8c714..58c5d17d1c 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -419,6 +419,32 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method,
JValue* result,
const mirror::MethodHandle::Kind handle_kind)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ // For virtual and interface methods ensure called_method points to
+ // the actual method to invoke.
+ if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual ||
+ handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) {
+ uint32_t receiver_reg = is_range ? first_arg : args[0];
+ ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(receiver_reg));
+ if (IsCallerTransformer(callsite_type)) {
+ // The current receiver is an emulated stack frame, the method's
+ // receiver needs to be fetched from there as the emulated frame
+ // will be unpacked into a new frame.
+ receiver = ObjPtr<mirror::EmulatedStackFrame>::DownCast(receiver)->GetReceiver();
+ }
+
+ ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass());
+ if (receiver == nullptr || receiver->GetClass() != declaring_class) {
+ // Verify that _vRegC is an object reference and of the type expected by
+ // the receiver.
+ if (!VerifyObjectIsClass(receiver, declaring_class)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
+ called_method, kRuntimePointerSize);
+ }
+ }
+
// Compute method information.
const DexFile::CodeItem* code_item = called_method->GetCodeItem();
@@ -502,24 +528,6 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method,
}
}
- // See TODO in DoInvokePolymorphic : We need to perform this dynamic, receiver
- // based dispatch right before we perform the actual call, because the
- // receiver isn't known very early.
- if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual ||
- handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) {
- ObjPtr<mirror::Object> receiver(new_shadow_frame->GetVRegReference(first_dest_reg));
- ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass());
- // Verify that _vRegC is an object reference and of the type expected by
- // the receiver.
- if (!VerifyObjectIsClass(receiver, declaring_class)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
-
- called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
- called_method, kRuntimePointerSize);
- }
-
PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
if (self->IsExceptionPending()) {
return false;
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 9a9a5d8398..eb2ec9b3c8 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -951,7 +951,8 @@ ObjPtr<Class> Class::GetDirectInterface(Thread* self, ObjPtr<Class> klass, uint3
return interfaces->Get(idx);
} else {
dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx);
- ObjPtr<Class> interface = klass->GetDexCache()->GetResolvedType(type_idx);
+ ObjPtr<Class> interface = ClassLinker::LookupResolvedType(
+ type_idx, klass->GetDexCache(), klass->GetClassLoader());
return interface;
}
}
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 973c8ed07d..29bf6a0240 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -41,14 +41,22 @@ inline uint32_t DexCache::ClassSize(PointerSize pointer_size) {
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
}
-inline mirror::String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
+inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) {
DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
- return StringDexCachePair::Lookup(GetStrings(), string_idx.index_, NumStrings()).Read();
+ const uint32_t slot_idx = string_idx.index_ % kDexCacheStringCacheSize;
+ DCHECK_LT(slot_idx, NumStrings());
+ return slot_idx;
}
-inline void DexCache::SetResolvedString(dex::StringIndex string_idx,
- ObjPtr<mirror::String> resolved) {
- StringDexCachePair::Assign(GetStrings(), string_idx.index_, resolved.Ptr(), NumStrings());
+inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
+ return GetStrings()[StringSlotIndex(string_idx)].load(
+ std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
+}
+
+inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
+ DCHECK(resolved != nullptr);
+ GetStrings()[StringSlotIndex(string_idx)].store(
+ StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed);
Runtime* const runtime = Runtime::Current();
if (UNLIKELY(runtime->IsActiveTransaction())) {
DCHECK(runtime->IsAotCompiler());
@@ -59,50 +67,70 @@ inline void DexCache::SetResolvedString(dex::StringIndex string_idx,
}
inline void DexCache::ClearString(dex::StringIndex string_idx) {
- const uint32_t slot_idx = string_idx.index_ % NumStrings();
DCHECK(Runtime::Current()->IsAotCompiler());
+ uint32_t slot_idx = StringSlotIndex(string_idx);
StringDexCacheType* slot = &GetStrings()[slot_idx];
// This is racy but should only be called from the transactional interpreter.
if (slot->load(std::memory_order_relaxed).index == string_idx.index_) {
- StringDexCachePair cleared(
- nullptr,
- StringDexCachePair::InvalidIndexForSlot(slot_idx));
+ StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx));
slot->store(cleared, std::memory_order_relaxed);
}
}
+inline uint32_t DexCache::TypeSlotIndex(dex::TypeIndex type_idx) {
+ DCHECK_LT(type_idx.index_, GetDexFile()->NumTypeIds());
+ const uint32_t slot_idx = type_idx.index_ % kDexCacheTypeCacheSize;
+ DCHECK_LT(slot_idx, NumResolvedTypes());
+ return slot_idx;
+}
+
inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
// It is theorized that a load acquire is not required since obtaining the resolved class will
// always have an address dependency or a lock.
- DCHECK_LT(type_idx.index_, NumResolvedTypes());
- return GetResolvedTypes()[type_idx.index_].Read();
+ return GetResolvedTypes()[TypeSlotIndex(type_idx)].load(
+ std::memory_order_relaxed).GetObjectForIndex(type_idx.index_);
}
inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
- DCHECK_LT(type_idx.index_, NumResolvedTypes()); // NOTE: Unchecked, i.e. not throwing AIOOB.
+ DCHECK(resolved != nullptr);
// TODO default transaction support.
// Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
// class but not necessarily seeing the loaded members like the static fields array.
// See b/32075261.
- reinterpret_cast<Atomic<GcRoot<mirror::Class>>&>(GetResolvedTypes()[type_idx.index_]).
- StoreRelease(GcRoot<Class>(resolved));
+ GetResolvedTypes()[TypeSlotIndex(type_idx)].store(
+ TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release);
// TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
}
-inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
- return MethodTypeDexCachePair::Lookup(
- GetResolvedMethodTypes(), proto_idx, NumResolvedMethodTypes()).Read();
+inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) {
+ DCHECK(Runtime::Current()->IsAotCompiler());
+ uint32_t slot_idx = TypeSlotIndex(type_idx);
+ TypeDexCacheType* slot = &GetResolvedTypes()[slot_idx];
+ // This is racy but should only be called from the single-threaded ImageWriter and tests.
+ if (slot->load(std::memory_order_relaxed).index == type_idx.index_) {
+ TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx));
+ slot->store(cleared, std::memory_order_relaxed);
+ }
}
-inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
+inline uint32_t DexCache::MethodTypeSlotIndex(uint32_t proto_idx) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
DCHECK_LT(proto_idx, GetDexFile()->NumProtoIds());
+ const uint32_t slot_idx = proto_idx % kDexCacheMethodTypeCacheSize;
+ DCHECK_LT(slot_idx, NumResolvedMethodTypes());
+ return slot_idx;
+}
- MethodTypeDexCachePair::Assign(GetResolvedMethodTypes(), proto_idx, resolved,
- NumResolvedMethodTypes());
+inline MethodType* DexCache::GetResolvedMethodType(uint32_t proto_idx) {
+ return GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].load(
+ std::memory_order_relaxed).GetObjectForIndex(proto_idx);
+}
+
+inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) {
+ DCHECK(resolved != nullptr);
+ GetResolvedMethodTypes()[MethodTypeSlotIndex(proto_idx)].store(
+ MethodTypeDexCachePair(resolved, proto_idx), std::memory_order_relaxed);
// TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this);
}
@@ -228,15 +256,13 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito
VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
// Visit arrays after.
if (kVisitNativeRoots) {
- VisitDexCachePairs<mirror::String, kReadBarrierOption, Visitor>(
+ VisitDexCachePairs<String, kReadBarrierOption, Visitor>(
GetStrings(), NumStrings(), visitor);
- GcRoot<mirror::Class>* resolved_types = GetResolvedTypes();
- for (size_t i = 0, num_types = NumResolvedTypes(); i != num_types; ++i) {
- visitor.VisitRootIfNonNull(resolved_types[i].AddressWithoutBarrier());
- }
+ VisitDexCachePairs<Class, kReadBarrierOption, Visitor>(
+ GetResolvedTypes(), NumResolvedTypes(), visitor);
- VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>(
+ VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>(
GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor);
GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites();
@@ -247,35 +273,37 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito
}
template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupStrings(mirror::StringDexCacheType* dest, const Visitor& visitor) {
- mirror::StringDexCacheType* src = GetStrings();
+inline void DexCache::FixupStrings(StringDexCacheType* dest, const Visitor& visitor) {
+ StringDexCacheType* src = GetStrings();
for (size_t i = 0, count = NumStrings(); i < count; ++i) {
StringDexCachePair source = src[i].load(std::memory_order_relaxed);
- mirror::String* ptr = source.object.Read<kReadBarrierOption>();
- mirror::String* new_source = visitor(ptr);
+ String* ptr = source.object.Read<kReadBarrierOption>();
+ String* new_source = visitor(ptr);
source.object = GcRoot<String>(new_source);
dest[i].store(source, std::memory_order_relaxed);
}
}
template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor) {
- GcRoot<mirror::Class>* src = GetResolvedTypes();
+inline void DexCache::FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor) {
+ TypeDexCacheType* src = GetResolvedTypes();
for (size_t i = 0, count = NumResolvedTypes(); i < count; ++i) {
- mirror::Class* source = src[i].Read<kReadBarrierOption>();
- mirror::Class* new_source = visitor(source);
- dest[i] = GcRoot<mirror::Class>(new_source);
+ TypeDexCachePair source = src[i].load(std::memory_order_relaxed);
+ Class* ptr = source.object.Read<kReadBarrierOption>();
+ Class* new_source = visitor(ptr);
+ source.object = GcRoot<Class>(new_source);
+ dest[i].store(source, std::memory_order_relaxed);
}
}
template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* dest,
+inline void DexCache::FixupResolvedMethodTypes(MethodTypeDexCacheType* dest,
const Visitor& visitor) {
- mirror::MethodTypeDexCacheType* src = GetResolvedMethodTypes();
+ MethodTypeDexCacheType* src = GetResolvedMethodTypes();
for (size_t i = 0, count = NumResolvedMethodTypes(); i < count; ++i) {
MethodTypeDexCachePair source = src[i].load(std::memory_order_relaxed);
- mirror::MethodType* ptr = source.object.Read<kReadBarrierOption>();
- mirror::MethodType* new_source = visitor(ptr);
+ MethodType* ptr = source.object.Read<kReadBarrierOption>();
+ MethodType* new_source = visitor(ptr);
source.object = GcRoot<MethodType>(new_source);
dest[i].store(source, std::memory_order_relaxed);
}
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index 0f6acab7e1..1b8b3913b9 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -58,8 +58,8 @@ void DexCache::InitializeDexCache(Thread* self,
mirror::StringDexCacheType* strings = (dex_file->NumStringIds() == 0u) ? nullptr :
reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
- GcRoot<mirror::Class>* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
- reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
+ mirror::TypeDexCacheType* types = (dex_file->NumTypeIds() == 0u) ? nullptr :
+ reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
ArtMethod** methods = (dex_file->NumMethodIds() == 0u) ? nullptr :
reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
ArtField** fields = (dex_file->NumFieldIds() == 0u) ? nullptr :
@@ -69,6 +69,10 @@ void DexCache::InitializeDexCache(Thread* self,
if (dex_file->NumStringIds() < num_strings) {
num_strings = dex_file->NumStringIds();
}
+ size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
+ if (dex_file->NumTypeIds() < num_types) {
+ num_types = dex_file->NumTypeIds();
+ }
// Note that we allocate the method type dex caches regardless of this flag,
// and we make sure here that they're not used by the runtime. This is in the
@@ -108,8 +112,9 @@ void DexCache::InitializeDexCache(Thread* self,
CHECK_EQ(strings[i].load(std::memory_order_relaxed).index, 0u);
CHECK(strings[i].load(std::memory_order_relaxed).object.IsNull());
}
- for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
- CHECK(types[i].IsNull());
+ for (size_t i = 0; i < num_types; ++i) {
+ CHECK_EQ(types[i].load(std::memory_order_relaxed).index, 0u);
+ CHECK(types[i].load(std::memory_order_relaxed).object.IsNull());
}
for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size) == nullptr);
@@ -128,6 +133,9 @@ void DexCache::InitializeDexCache(Thread* self,
if (strings != nullptr) {
mirror::StringDexCachePair::Initialize(strings);
}
+ if (types != nullptr) {
+ mirror::TypeDexCachePair::Initialize(types);
+ }
if (method_types != nullptr) {
mirror::MethodTypeDexCachePair::Initialize(method_types);
}
@@ -136,7 +144,7 @@ void DexCache::InitializeDexCache(Thread* self,
strings,
num_strings,
types,
- dex_file->NumTypeIds(),
+ num_types,
methods,
dex_file->NumMethodIds(),
fields,
@@ -152,7 +160,7 @@ void DexCache::Init(const DexFile* dex_file,
ObjPtr<String> location,
StringDexCacheType* strings,
uint32_t num_strings,
- GcRoot<Class>* resolved_types,
+ TypeDexCacheType* resolved_types,
uint32_t num_resolved_types,
ArtMethod** resolved_methods,
uint32_t num_resolved_methods,
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 10bb5aa01c..057919806f 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -18,14 +18,14 @@
#define ART_RUNTIME_MIRROR_DEX_CACHE_H_
#include "array.h"
-#include "art_field.h"
-#include "class.h"
+#include "base/bit_utils.h"
#include "dex_file_types.h"
#include "object.h"
#include "object_array.h"
namespace art {
+class ArtField;
class ArtMethod;
struct DexCacheOffsets;
class DexFile;
@@ -37,6 +37,7 @@ class Thread;
namespace mirror {
class CallSite;
+class Class;
class MethodType;
class String;
@@ -61,7 +62,7 @@ template <typename T> struct PACKED(8) DexCachePair {
// it's always non-null if the id branch succeeds (except for the 0th id).
// Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
// the lookup id == stored id branch.
- DexCachePair(T* object, uint32_t index)
+ DexCachePair(ObjPtr<T> object, uint32_t index)
: object(object),
index(index) {}
DexCachePair() = default;
@@ -75,39 +76,28 @@ template <typename T> struct PACKED(8) DexCachePair {
dex_cache[0].store(first_elem, std::memory_order_relaxed);
}
- static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
- uint32_t idx,
- uint32_t cache_size) {
- DCHECK_NE(cache_size, 0u);
- DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
- if (idx != element.index) {
- return GcRoot<T>(nullptr);
- }
-
- DCHECK(!element.object.IsNull());
- return element.object;
- }
-
- static void Assign(std::atomic<DexCachePair<T>>* dex_cache,
- uint32_t idx,
- T* object,
- uint32_t cache_size) {
- DCHECK_LT(idx % cache_size, cache_size);
- dex_cache[idx % cache_size].store(
- DexCachePair<T>(object, idx), std::memory_order_relaxed);
- }
-
static uint32_t InvalidIndexForSlot(uint32_t slot) {
// Since the cache size is a power of two, 0 will always map to slot 0.
// Use 1 for slot 0 and 0 for all other slots.
return (slot == 0) ? 1u : 0u;
}
+
+ T* GetObjectForIndex(uint32_t idx) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (idx != index) {
+ return nullptr;
+ }
+ DCHECK(!object.IsNull());
+ return object.Read();
+ }
};
-using StringDexCachePair = DexCachePair<mirror::String>;
+using TypeDexCachePair = DexCachePair<Class>;
+using TypeDexCacheType = std::atomic<TypeDexCachePair>;
+
+using StringDexCachePair = DexCachePair<String>;
using StringDexCacheType = std::atomic<StringDexCachePair>;
-using MethodTypeDexCachePair = DexCachePair<mirror::MethodType>;
+using MethodTypeDexCachePair = DexCachePair<MethodType>;
using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
// C++ mirror of java.lang.DexCache.
@@ -116,6 +106,11 @@ class MANAGED DexCache FINAL : public Object {
// Size of java.lang.DexCache.class.
static uint32_t ClassSize(PointerSize pointer_size);
+ // Size of type dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
+ static constexpr size_t kDexCacheTypeCacheSize = 1024;
+ static_assert(IsPowerOfTwo(kDexCacheTypeCacheSize),
+ "Type dex cache size is not a power of 2.");
+
// Size of string dex cache. Needs to be a power of 2 for entrypoint assumptions to hold.
static constexpr size_t kDexCacheStringCacheSize = 1024;
static_assert(IsPowerOfTwo(kDexCacheStringCacheSize),
@@ -127,6 +122,10 @@ class MANAGED DexCache FINAL : public Object {
static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
"MethodType dex cache size is not a power of 2.");
+ static constexpr size_t StaticTypeSize() {
+ return kDexCacheTypeCacheSize;
+ }
+
static constexpr size_t StaticStringSize() {
return kDexCacheStringCacheSize;
}
@@ -157,7 +156,7 @@ class MANAGED DexCache FINAL : public Object {
REQUIRES_SHARED(Locks::mutator_lock_);
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
- void FixupResolvedTypes(GcRoot<mirror::Class>* dest, const Visitor& visitor)
+ void FixupResolvedTypes(TypeDexCacheType* dest, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
@@ -224,7 +223,7 @@ class MANAGED DexCache FINAL : public Object {
return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
}
- mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
+ String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
void SetResolvedString(dex::StringIndex string_idx, ObjPtr<mirror::String> resolved) ALWAYS_INLINE
@@ -239,6 +238,8 @@ class MANAGED DexCache FINAL : public Object {
void SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void ClearResolvedType(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
ALWAYS_INLINE ArtMethod* GetResolvedMethod(uint32_t method_idx, PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -278,11 +279,11 @@ class MANAGED DexCache FINAL : public Object {
SetFieldPtr<false>(StringsOffset(), strings);
}
- GcRoot<Class>* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<GcRoot<Class>*>(ResolvedTypesOffset());
+ TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldPtr<TypeDexCacheType*>(ResolvedTypesOffset());
}
- void SetResolvedTypes(GcRoot<Class>* resolved_types)
+ void SetResolvedTypes(TypeDexCacheType* resolved_types)
ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_) {
SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
@@ -363,7 +364,7 @@ class MANAGED DexCache FINAL : public Object {
SetFieldPtr<false>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file);
}
- void SetLocation(ObjPtr<mirror::String> location) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_);
// NOTE: Get/SetElementPtrSize() are intended for working with ArtMethod** and ArtField**
// provided by GetResolvedMethods/Fields() and ArtMethod::GetDexCacheResolvedMethods(),
@@ -380,7 +381,7 @@ class MANAGED DexCache FINAL : public Object {
ObjPtr<String> location,
StringDexCacheType* strings,
uint32_t num_strings,
- GcRoot<Class>* resolved_types,
+ TypeDexCacheType* resolved_types,
uint32_t num_resolved_types,
ArtMethod** resolved_methods,
uint32_t num_resolved_methods,
@@ -393,12 +394,16 @@ class MANAGED DexCache FINAL : public Object {
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t MethodTypeSlotIndex(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Visit instance fields of the dex cache as well as its associated arrays.
template <bool kVisitNativeRoots,
VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
typename Visitor>
- void VisitReferences(ObjPtr<mirror::Class> klass, const Visitor& visitor)
+ void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
HeapReference<Object> dex_;
@@ -410,7 +415,7 @@ class MANAGED DexCache FINAL : public Object {
uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
// num_resolved_method_types_ elements.
uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
- uint64_t resolved_types_; // GcRoot<Class>*, array with num_resolved_types_ elements.
+ uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements.
uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
// elements.
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 5a2ab7151c..ef0aaaaa70 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -51,7 +51,8 @@ TEST_F(DexCacheTest, Open) {
EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings()
|| java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings());
- EXPECT_EQ(java_lang_dex_file_->NumTypeIds(), dex_cache->NumResolvedTypes());
+ EXPECT_TRUE(dex_cache->StaticTypeSize() == dex_cache->NumResolvedTypes()
+ || java_lang_dex_file_->NumTypeIds() == dex_cache->NumResolvedTypes());
EXPECT_EQ(java_lang_dex_file_->NumMethodIds(), dex_cache->NumResolvedMethods());
EXPECT_EQ(java_lang_dex_file_->NumFieldIds(), dex_cache->NumResolvedFields());
EXPECT_TRUE(dex_cache->StaticMethodTypeSize() == dex_cache->NumResolvedMethodTypes()
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
index ddd84a167d..76859ef1c8 100644
--- a/runtime/mirror/emulated_stack_frame.h
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -62,6 +62,10 @@ class MANAGED EmulatedStackFrame : public Object {
return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_));
}
+ mirror::Object* GetReceiver() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetReferences()->Get(0);
+ }
+
static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 1234933db9..0e61cf64f9 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -155,6 +155,105 @@ class NullableScopedUtfChars {
void operator=(const NullableScopedUtfChars&);
};
+static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
+ if (end <= start) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("Bad range");
+ return nullptr;
+ }
+
+ std::string error_message;
+ size_t length = static_cast<size_t>(end - start);
+ std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
+ nullptr,
+ length,
+ PROT_READ | PROT_WRITE,
+ /* low_4gb */ false,
+ /* reuse */ false,
+ &error_message));
+ if (dex_mem_map == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("%s", error_message.c_str());
+ }
+ return dex_mem_map;
+}
+
+static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
+ std::string location = StringPrintf("Anonymous-DexFile@%p-%p",
+ dex_mem_map->Begin(),
+ dex_mem_map->End());
+ std::string error_message;
+ std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
+ 0,
+ std::move(dex_mem_map),
+ /* verify */ true,
+ /* verify_location */ true,
+ &error_message));
+ if (dex_file == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("%s", error_message.c_str());
+ return nullptr;
+ }
+
+ if (!dex_file->DisableWrite()) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("Failed to make dex file read-only");
+ return nullptr;
+ }
+
+ return dex_file.release();
+}
+
+static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data) {
+ std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(data)));
+ if (dex_file.get() == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files.push_back(std::move(dex_file));
+ return ConvertDexFilesToJavaArray(env, nullptr, dex_files);
+}
+
+static jobject DexFile_createCookieWithDirectBuffer(JNIEnv* env,
+ jclass,
+ jobject buffer,
+ jint start,
+ jint end) {
+ uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
+ if (base_address == nullptr) {
+ ScopedObjectAccess soa(env);
+ ThrowWrappedIOException("dexFileBuffer not direct");
+ return 0;
+ }
+
+ std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+ if (dex_mem_map == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return 0;
+ }
+
+ size_t length = static_cast<size_t>(end - start);
+ memcpy(dex_mem_map->Begin(), base_address, length);
+ return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
+}
+
+static jobject DexFile_createCookieWithArray(JNIEnv* env,
+ jclass,
+ jbyteArray buffer,
+ jint start,
+ jint end) {
+ std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
+ if (dex_mem_map == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return 0;
+ }
+
+ auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
+ env->GetByteArrayRegion(buffer, start, end - start, destination);
+ return CreateSingleDexFileCookie(env, std::move(dex_mem_map));
+}
+
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
@@ -591,6 +690,9 @@ static JNINativeMethod gMethods[] = {
"Ljava/lang/ClassLoader;"
"[Ldalvik/system/DexPathList$Element;"
")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile, createCookieWithDirectBuffer,
+ "(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile, createCookieWithArray, "([BII)Ljava/lang/Object;"),
NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile,
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
deleted file mode 100644
index 07959607fc..0000000000
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dalvik_system_InMemoryDexClassLoader_DexData.h"
-
-#include "android-base/stringprintf.h"
-
-#include "class_linker.h"
-#include "common_throws.h"
-#include "dex_file.h"
-#include "jni_internal.h"
-#include "mem_map.h"
-#include "mirror/class_loader.h"
-#include "mirror/object-inl.h"
-#include "oat_file.h"
-#include "scoped_thread_state_change-inl.h"
-#include "ScopedUtfChars.h"
-
-namespace art {
-
-using android::base::StringPrintf;
-
-static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
- if (end <= start) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("Bad range");
- return nullptr;
- }
-
- std::string error_message;
- size_t length = static_cast<size_t>(end - start);
- std::unique_ptr<MemMap> dex_mem_map(MemMap::MapAnonymous("DEX data",
- nullptr,
- length,
- PROT_READ | PROT_WRITE,
- /* low_4gb */ false,
- /* reuse */ false,
- &error_message));
- if (dex_mem_map == nullptr) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("%s", error_message.c_str());
- }
- return dex_mem_map;
-}
-
-static jlong DexFileToCookie(const DexFile* dex_file) {
- return reinterpret_cast<jlong>(dex_file);
-}
-
-static const DexFile* CookieToDexFile(jlong cookie) {
- return reinterpret_cast<const DexFile*>(cookie);
-}
-
-static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map) {
- std::string location = StringPrintf("InMemoryDexClassLoader_DexData@%p-%p",
- dex_mem_map->Begin(),
- dex_mem_map->End());
- std::string error_message;
- std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
- 0,
- std::move(dex_mem_map),
- /* verify */ true,
- /* verify_location */ true,
- &error_message));
- if (dex_file == nullptr) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("%s", error_message.c_str());
- return nullptr;
- }
-
- if (!dex_file->DisableWrite()) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("Failed to make dex file read-only");
- return nullptr;
- }
-
- return dex_file.release();
-}
-
-static jlong InMemoryDexClassLoader_DexData_initializeWithDirectBuffer(
- JNIEnv* env, jclass, jobject buffer, jint start, jint end) {
- uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
- if (base_address == nullptr) {
- ScopedObjectAccess soa(env);
- ThrowWrappedIOException("dexFileBuffer not direct");
- return 0;
- }
-
- std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
- if (dex_mem_map == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return 0;
- }
-
- size_t length = static_cast<size_t>(end - start);
- memcpy(dex_mem_map->Begin(), base_address, length);
- return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
-}
-
-static jlong InMemoryDexClassLoader_DexData_initializeWithArray(
- JNIEnv* env, jclass, jbyteArray buffer, jint start, jint end) {
- std::unique_ptr<MemMap> dex_mem_map(AllocateDexMemoryMap(env, start, end));
- if (dex_mem_map == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return 0;
- }
-
- auto destination = reinterpret_cast<jbyte*>(dex_mem_map.get()->Begin());
- env->GetByteArrayRegion(buffer, start, end - start, destination);
- return DexFileToCookie(CreateDexFile(env, std::move(dex_mem_map)));
-}
-
-static void InMemoryDexClassLoader_DexData_uninitialize(JNIEnv* env, jclass, jlong cookie) {
- const DexFile* dex_file = CookieToDexFile(cookie);
- if (kIsDebugBuild) {
- ScopedObjectAccess soa(env);
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- CHECK(!class_linker->IsDexFileRegistered(soa.Self(), *dex_file));
- }
- delete dex_file;
-}
-
-static jclass InMemoryDexClassLoader_DexData_findClass(
- JNIEnv* env, jobject dexData, jstring name, jobject loader, jlong cookie) {
- ScopedUtfChars scoped_class_name(env, name);
- if (env->ExceptionCheck()) {
- return nullptr;
- }
-
- const char* class_name = scoped_class_name.c_str();
- const std::string descriptor(DotToDescriptor(class_name));
- const char* class_descriptor = descriptor.c_str();
- const size_t hash = ComputeModifiedUtf8Hash(class_descriptor);
- const DexFile* dex_file = CookieToDexFile(cookie);
- const DexFile::ClassDef* dex_class_def =
- OatDexFile::FindClassDef(*dex_file, class_descriptor, hash);
- if (dex_class_def != nullptr) {
- ScopedObjectAccess soa(env);
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<1> handle_scope(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- handle_scope.NewHandle(soa.Decode<mirror::ClassLoader>(loader)));
- ObjPtr<mirror::DexCache> dex_cache =
- class_linker->RegisterDexFile(*dex_file, class_loader.Get());
- if (dex_cache == nullptr) {
- // OOME or InternalError (dexFile already registered with a different class loader).
- soa.Self()->AssertPendingException();
- return nullptr;
- }
- ObjPtr<mirror::Class> result = class_linker->DefineClass(
- soa.Self(),
- class_descriptor,
- hash, class_loader,
- *dex_file,
- *dex_class_def);
- if (result != nullptr) {
- // Ensure the class table has a strong reference to the
- // InMemoryClassLoader/DexData instance now that a class has
- // been loaded.
- class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexData),
- class_loader.Get());
- return soa.AddLocalReference<jclass>(result);
- }
- }
-
- VLOG(class_linker) << "Failed to find dex_class_def " << class_name;
- return nullptr;
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(InMemoryDexClassLoader_DexData,
- initializeWithDirectBuffer,
- "(Ljava/nio/ByteBuffer;II)J"),
- NATIVE_METHOD(InMemoryDexClassLoader_DexData, initializeWithArray, "([BII)J"),
- NATIVE_METHOD(InMemoryDexClassLoader_DexData, uninitialize, "(J)V"),
- NATIVE_METHOD(InMemoryDexClassLoader_DexData,
- findClass,
- "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
-};
-
-void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env) {
- REGISTER_NATIVE_METHODS("dalvik/system/InMemoryDexClassLoader$DexData");
-}
-
-} // namespace art
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index b1ed74a6de..ee6dda56a5 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -53,7 +53,7 @@ static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
static jobject DexCache_getResolvedType(JNIEnv* env, jobject javaDexCache, jint type_index) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::DexCache>(javaDexCache);
- CHECK_LT(static_cast<size_t>(type_index), dex_cache->NumResolvedTypes());
+ CHECK_LT(static_cast<size_t>(type_index), dex_cache->GetDexFile()->NumTypeIds());
return soa.AddLocalReference<jobject>(dex_cache->GetResolvedType(dex::TypeIndex(type_index)));
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 656b86812e..1544121ed1 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '1', '1', '3', '\0' }; // Invoke info change.
+ static constexpr uint8_t kOatVersion[] = { '1', '1', '4', '\0' }; // hash-based DexCache types.
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 7ca233fb10..a8a0ded7dc 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -41,6 +41,7 @@
#include "class_table-inl.h"
#include "class_linker.h"
#include "common_throws.h"
+#include "dex_file_annotations.h"
#include "events-inl.h"
#include "gc/heap.h"
#include "gc_root.h"
@@ -50,6 +51,7 @@
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
+#include "mirror/object_array-inl.h"
#include "mirror/object_reference.h"
#include "mirror/object-inl.h"
#include "mirror/reference.h"
@@ -685,9 +687,30 @@ jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env,
*signature_ptr = reinterpret_cast<char*>(tmp);
}
- // TODO: Support generic signature.
if (generic_ptr != nullptr) {
*generic_ptr = nullptr;
+ if (!klass->IsProxyClass() && klass->GetDexCache() != nullptr) {
+ art::StackHandleScope<1> hs(soa.Self());
+ art::Handle<art::mirror::Class> h_klass = hs.NewHandle(klass);
+ art::mirror::ObjectArray<art::mirror::String>* str_array =
+ art::annotations::GetSignatureAnnotationForClass(h_klass);
+ if (str_array != nullptr) {
+ std::ostringstream oss;
+ for (int32_t i = 0; i != str_array->GetLength(); ++i) {
+ oss << str_array->Get(i)->ToModifiedUtf8();
+ }
+ std::string output_string = oss.str();
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, output_string.c_str(), &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ *generic_ptr = reinterpret_cast<char*>(tmp);
+ } else if (soa.Self()->IsExceptionPending()) {
+ // TODO: Should we report an error here?
+ soa.Self()->ClearException();
+ }
+ }
}
// Everything is fine, release the buffers.
diff --git a/runtime/openjdkjvmti/ti_field.cc b/runtime/openjdkjvmti/ti_field.cc
index a762830d85..131e6c3632 100644
--- a/runtime/openjdkjvmti/ti_field.cc
+++ b/runtime/openjdkjvmti/ti_field.cc
@@ -34,7 +34,9 @@
#include "art_jvmti.h"
#include "art_field-inl.h"
#include "base/enums.h"
+#include "dex_file_annotations.h"
#include "jni_internal.h"
+#include "mirror/object_array-inl.h"
#include "modifiers.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
@@ -91,6 +93,26 @@ jvmtiError FieldUtil::GetFieldName(jvmtiEnv* env,
// TODO: Support generic signature.
if (generic_ptr != nullptr) {
*generic_ptr = nullptr;
+ if (!art_field->GetDeclaringClass()->IsProxyClass()) {
+ art::mirror::ObjectArray<art::mirror::String>* str_array =
+ art::annotations::GetSignatureAnnotationForField(art_field);
+ if (str_array != nullptr) {
+ std::ostringstream oss;
+ for (int32_t i = 0; i != str_array->GetLength(); ++i) {
+ oss << str_array->Get(i)->ToModifiedUtf8();
+ }
+ std::string output_string = oss.str();
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, output_string.c_str(), &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ *generic_ptr = reinterpret_cast<char*>(tmp);
+ } else if (soa.Self()->IsExceptionPending()) {
+ // TODO: Should we report an error here?
+ soa.Self()->ClearException();
+ }
+ }
}
// Everything is fine, release the buffers.
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index 2ddd64a2bc..a6cfcc12bc 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -34,7 +34,9 @@
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "base/enums.h"
+#include "dex_file_annotations.h"
#include "jni_internal.h"
+#include "mirror/object_array-inl.h"
#include "modifiers.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
@@ -139,6 +141,26 @@ jvmtiError MethodUtil::GetMethodName(jvmtiEnv* env,
// TODO: Support generic signature.
if (generic_ptr != nullptr) {
*generic_ptr = nullptr;
+ if (!art_method->GetDeclaringClass()->IsProxyClass()) {
+ art::mirror::ObjectArray<art::mirror::String>* str_array =
+ art::annotations::GetSignatureAnnotationForMethod(art_method);
+ if (str_array != nullptr) {
+ std::ostringstream oss;
+ for (int32_t i = 0; i != str_array->GetLength(); ++i) {
+ oss << str_array->Get(i)->ToModifiedUtf8();
+ }
+ std::string output_string = oss.str();
+ unsigned char* tmp;
+ jvmtiError ret = CopyString(env, output_string.c_str(), &tmp);
+ if (ret != ERR(NONE)) {
+ return ret;
+ }
+ *generic_ptr = reinterpret_cast<char*>(tmp);
+ } else if (soa.Self()->IsExceptionPending()) {
+ // TODO: Should we report an error here?
+ soa.Self()->ClearException();
+ }
+ }
}
// Everything is fine, release the buffers.
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index f01acc11aa..8436045072 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -747,8 +747,6 @@ bool Redefiner::ClassRedefinition::CheckClass() {
}
}
}
- LOG(WARNING) << "No verification is done on annotations of redefined classes.";
-
return true;
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f8f3d766c0..69dcfebcb1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -101,7 +101,6 @@
#include "mirror/throwable.h"
#include "monitor.h"
#include "native/dalvik_system_DexFile.h"
-#include "native/dalvik_system_InMemoryDexClassLoader_DexData.h"
#include "native/dalvik_system_VMDebug.h"
#include "native/dalvik_system_VMRuntime.h"
#include "native/dalvik_system_VMStack.h"
@@ -1534,7 +1533,6 @@ jobject Runtime::GetSystemClassLoader() const {
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_dalvik_system_DexFile(env);
- register_dalvik_system_InMemoryDexClassLoader_DexData(env);
register_dalvik_system_VMDebug(env);
register_dalvik_system_VMRuntime(env);
register_dalvik_system_VMStack(env);
diff --git a/runtime/safe_map.h b/runtime/safe_map.h
index 49f80f31a8..e638fdb504 100644
--- a/runtime/safe_map.h
+++ b/runtime/safe_map.h
@@ -137,6 +137,16 @@ class SafeMap {
return it->second;
}
+ iterator FindOrAdd(const K& k, const V& v) {
+ iterator it = find(k);
+ return it == end() ? Put(k, v) : it;
+ }
+
+ iterator FindOrAdd(const K& k) {
+ iterator it = find(k);
+ return it == end() ? Put(k, V()) : it;
+ }
+
bool Equals(const Self& rhs) const {
return map_ == rhs.map_;
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3abb9fca7b..ff66cc1697 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2549,11 +2549,18 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(
return nullptr;
}
const char* source_file = method->GetDeclaringClassSourceFile();
- if (source_file != nullptr) {
- source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
- if (source_name_object == nullptr) {
- soa.Self()->AssertPendingOOMException();
- return nullptr;
+ if (line_number == -1) {
+ // Make the line_number field of StackTraceElement hold the dex pc.
+ // source_name_object is intentionally left null if we failed to map the dex pc to
+ // a line number (most probably because there is no debug info). See b/30183883.
+ line_number = dex_pc;
+ } else {
+ if (source_file != nullptr) {
+ source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
+ if (source_name_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
}
}
}
@@ -2564,11 +2571,11 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(
if (method_name_object == nullptr) {
return nullptr;
}
- ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(),
- class_name_object,
- method_name_object,
- source_name_object,
- line_number);
+ ObjPtr<mirror::StackTraceElement> obj = mirror::StackTraceElement::Alloc(soa.Self(),
+ class_name_object,
+ method_name_object,
+ source_name_object,
+ line_number);
if (obj == nullptr) {
return nullptr;
}
diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h
index 98658215f7..f9a1405354 100644
--- a/runtime/utils/dex_cache_arrays_layout-inl.h
+++ b/runtime/utils/dex_cache_arrays_layout-inl.h
@@ -51,9 +51,11 @@ inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, cons
: DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) {
}
-inline constexpr size_t DexCacheArraysLayout::Alignment() {
- // GcRoot<> alignment is 4, i.e. lower than or equal to the pointer alignment.
- static_assert(alignof(GcRoot<mirror::Class>) == 4, "Expecting alignof(GcRoot<>) == 4");
+constexpr size_t DexCacheArraysLayout::Alignment() {
+ // mirror::Type/String/MethodTypeDexCacheType alignment is 8,
+ // i.e. higher than or equal to the pointer alignment.
+ static_assert(alignof(mirror::TypeDexCacheType) == 8,
+ "Expecting alignof(ClassDexCacheType) == 8");
static_assert(alignof(mirror::StringDexCacheType) == 8,
"Expecting alignof(StringDexCacheType) == 8");
static_assert(alignof(mirror::MethodTypeDexCacheType) == 8,
@@ -63,17 +65,22 @@ inline constexpr size_t DexCacheArraysLayout::Alignment() {
}
template <typename T>
-static constexpr PointerSize GcRootAsPointerSize() {
+constexpr PointerSize GcRootAsPointerSize() {
static_assert(sizeof(GcRoot<T>) == 4U, "Unexpected GcRoot size");
return PointerSize::k32;
}
inline size_t DexCacheArraysLayout::TypeOffset(dex::TypeIndex type_idx) const {
- return types_offset_ + ElementOffset(GcRootAsPointerSize<mirror::Class>(), type_idx.index_);
+ return types_offset_ + ElementOffset(PointerSize::k64,
+ type_idx.index_ % mirror::DexCache::kDexCacheTypeCacheSize);
}
inline size_t DexCacheArraysLayout::TypesSize(size_t num_elements) const {
- return ArraySize(GcRootAsPointerSize<mirror::Class>(), num_elements);
+ size_t cache_size = mirror::DexCache::kDexCacheTypeCacheSize;
+ if (num_elements < cache_size) {
+ cache_size = num_elements;
+ }
+ return ArraySize(PointerSize::k64, cache_size);
}
inline size_t DexCacheArraysLayout::TypesAlignment() const {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 16739fa3bc..38d151bc67 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2399,7 +2399,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) {
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);
+ ObjPtr<mirror::Class> klass =
+ ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get());
if (klass != nullptr && klass->IsPrimitive()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type "
<< dex_file_->StringByTypeIdx(type_idx) << " in instanceof in "
@@ -3722,9 +3723,16 @@ inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) {
}
const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_idx) {
- mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
+ mirror::Class* klass = can_load_classes_
+ ? Runtime::Current()->GetClassLinker()->ResolveType(
+ *dex_file_, class_idx, dex_cache_, class_loader_)
+ : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()).Ptr();
+ if (can_load_classes_ && klass == nullptr) {
+ DCHECK(self_->IsExceptionPending());
+ self_->ClearException();
+ }
const RegType* result = nullptr;
- if (klass != nullptr) {
+ if (klass != nullptr && !klass->IsErroneous()) {
bool precise = klass->CannotBeAssignedFromOtherTypes();
if (precise && !IsInstantiableOrPrimitive(klass)) {
const char* descriptor = dex_file_->StringByTypeIdx(class_idx);
@@ -3747,10 +3755,6 @@ const RegType& MethodVerifier::ResolveClassAndCheckAccess(dex::TypeIndex class_i
<< "' in " << GetDeclaringClass();
return *result;
}
- if (klass == nullptr && !result->IsUnresolvedTypes()) {
- klass = result->GetClass();
- dex_cache_->SetResolvedType(class_idx, klass);
- }
// Record result of class resolution attempt.
VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass);
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 5a43a4f23f..0dd82abf6f 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -16,6 +16,7 @@
import junit.framework.Assert;
import java.lang.reflect.Method;
+import java.util.Locale;
/**
* more string tests
@@ -120,6 +121,12 @@ public class Main {
testEqualsConstString();
testConstStringEquals();
+
+ // Regression tests for String.setCharAt() breaking string compression invariants.
+ Locale en_US = new Locale("en", "US");
+ Assert.assertEquals("I", /* Small latin dotless i */ "\u0131".toUpperCase());
+ Assert.assertEquals("abc", "a\u0131c".replace('\u0131', 'b'));
+ Assert.assertEquals("a\u0131c", "abc".replace('b', '\u0131'));
}
public static void testCompareToAndEquals() {
diff --git a/test/155-java-set-resolved-type/src/Main.java b/test/155-java-set-resolved-type/src/Main.java
index f92363e915..56b8c3ece9 100644
--- a/test/155-java-set-resolved-type/src/Main.java
+++ b/test/155-java-set-resolved-type/src/Main.java
@@ -55,11 +55,7 @@ public class Main {
Class<?> timpl = Class.forName("TestImplementation", false, mainLoader);
// Clear the dex cache resolved types to force a proper lookup the next time
// we need to find TestInterface.
- // TODO: Enable clearing the dex cache when we switch to the hash-based type array
- // and do a proper lookup. Currently, ClassLinker fully relies on the DexCache.
- if (false) {
- clearResolvedTypes(timpl);
- }
+ clearResolvedTypes(timpl);
// Force intialization of TestClass2. This expects the interface type to be
// resolved and found through simple lookup.
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index a4561b83da..e9673987da 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -76,6 +76,25 @@ public class Main {
* the shifter operand.
*/
+ /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (before)
+ /// CHECK-DAG: <<l:j\d+>> ParameterValue
+ /// CHECK-DAG: <<b:b\d+>> ParameterValue
+ /// CHECK: <<tmp:j\d+>> TypeConversion [<<b>>]
+ /// CHECK: Sub [<<l>>,<<tmp>>]
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (after)
+ /// CHECK-DAG: <<l:j\d+>> ParameterValue
+ /// CHECK-DAG: <<b:b\d+>> ParameterValue
+ /// CHECK: DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm (after)
+ /// CHECK-NOT: TypeConversion
+ /// CHECK-NOT: Sub
+
+ /// CHECK-START-ARM: long Main.$opt$noinline$translate(long, byte) disassembly (after)
+ /// CHECK: subs r{{\d+}}, r{{\d+}}, r{{\d+}}
+ /// CHECK: sbc r{{\d+}}, r{{\d+}}, r{{\d+}}, asr #31
+
/// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (before)
/// CHECK-DAG: <<l:j\d+>> ParameterValue
/// CHECK-DAG: <<b:b\d+>> ParameterValue
@@ -85,7 +104,7 @@ public class Main {
/// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after)
/// CHECK-DAG: <<l:j\d+>> ParameterValue
/// CHECK-DAG: <<b:b\d+>> ParameterValue
- /// CHECK: Arm64DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB
+ /// CHECK: DataProcWithShifterOp [<<l>>,<<b>>] kind:Sub+SXTB
/// CHECK-START-ARM64: long Main.$opt$noinline$translate(long, byte) instruction_simplifier_arm64 (after)
/// CHECK-NOT: TypeConversion
@@ -106,6 +125,21 @@ public class Main {
* inputs are the the IR.
*/
+ /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (before)
+ /// CHECK: <<a:i\d+>> ParameterValue
+ /// CHECK: <<Const2:i\d+>> IntConstant 2
+ /// CHECK: <<tmp:i\d+>> Shl [<<a>>,<<Const2>>]
+ /// CHECK: Add [<<tmp>>,<<tmp>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (after)
+ /// CHECK-DAG: <<a:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+ /// CHECK: <<Shl:i\d+>> Shl [<<a>>,<<Const2>>]
+ /// CHECK: Add [<<Shl>>,<<Shl>>]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (before)
/// CHECK: <<a:i\d+>> ParameterValue
/// CHECK: <<Const2:i\d+>> IntConstant 2
@@ -119,7 +153,7 @@ public class Main {
/// CHECK: Add [<<Shl>>,<<Shl>>]
/// CHECK-START-ARM64: int Main.$opt$noinline$sameInput(int) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
public static int $opt$noinline$sameInput(int a) {
if (doThrow) throw new Error();
@@ -131,6 +165,28 @@ public class Main {
* Check that we perform the merge for multiple uses.
*/
+ /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (before)
+ /// CHECK: <<arg:i\d+>> ParameterValue
+ /// CHECK: <<Const23:i\d+>> IntConstant 23
+ /// CHECK: <<tmp:i\d+>> Shl [<<arg>>,<<Const23>>]
+ /// CHECK: Add [<<tmp>>,{{i\d+}}]
+ /// CHECK: Add [<<tmp>>,{{i\d+}}]
+ /// CHECK: Add [<<tmp>>,{{i\d+}}]
+ /// CHECK: Add [<<tmp>>,{{i\d+}}]
+ /// CHECK: Add [<<tmp>>,{{i\d+}}]
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (after)
+ /// CHECK: <<arg:i\d+>> ParameterValue
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+
+ /// CHECK-START-ARM: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: Shl
+ /// CHECK-NOT: Add
+
/// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (before)
/// CHECK: <<arg:i\d+>> ParameterValue
/// CHECK: <<Const23:i\d+>> IntConstant 23
@@ -143,11 +199,11 @@ public class Main {
/// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after)
/// CHECK: <<arg:i\d+>> ParameterValue
- /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
- /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
- /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
- /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
- /// CHECK: Arm64DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
+ /// CHECK: DataProcWithShifterOp [{{i\d+}},<<arg>>] kind:Add+LSL shift:23
/// CHECK-START-ARM64: int Main.$opt$noinline$multipleUses(int) instruction_simplifier_arm64 (after)
/// CHECK-NOT: Shl
@@ -171,9 +227,19 @@ public class Main {
* operand, so test that only the shifts are merged.
*/
+ /// CHECK-START-ARM: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$noinline$testAnd(long, long) disassembly (after)
+ /// CHECK: and lsl
+ /// CHECK: sbfx
+ /// CHECK: asr
+ /// CHECK: and
+
/// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$noinline$testAnd(long, long) disassembly (after)
/// CHECK: and lsl
@@ -186,9 +252,18 @@ public class Main {
(a & (b << 5)) | (a & (byte)b));
}
+ /// CHECK-START-ARM: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$noinline$testOr(int, int) disassembly (after)
+ /// CHECK: orr asr
+ /// CHECK: ubfx
+ /// CHECK: orr
+
/// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$noinline$testOr(int, int) disassembly (after)
/// CHECK: orr asr
@@ -201,9 +276,19 @@ public class Main {
(a | (b >> 6)) | (a | (char)b));
}
+ /// CHECK-START-ARM: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$noinline$testXor(long, long) disassembly (after)
+ /// CHECK: eor lsr
+ /// CHECK: mov
+ /// CHECK: asr
+ /// CHECK: eor
+
/// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$noinline$testXor(long, long) disassembly (after)
/// CHECK: eor lsr
@@ -216,9 +301,12 @@ public class Main {
(a ^ (b >>> 7)) | (a ^ (int)b));
}
+ /// CHECK-START-ARM: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$noinline$testNeg(int) disassembly (after)
/// CHECK: neg lsl
@@ -239,9 +327,12 @@ public class Main {
* does occur on the right-hand.
*/
+ /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after)
/// CHECK-NOT: TypeConversion
@@ -252,9 +343,11 @@ public class Main {
assertIntEquals(a + $noinline$byteToShort(b), a + (short)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt2(int, byte) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
public static void $opt$validateExtendByteInt2(int a, byte b) {
// The conversion to `int` has been optimized away, so there is nothing to merge.
@@ -263,13 +356,25 @@ public class Main {
assertLongEquals(a + $noinline$byteToLong(b), a + (long)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm (after)
+ /// CHECK: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
@@ -294,9 +399,12 @@ public class Main {
$opt$validateExtendByteLong(a, b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt1(int, char) instruction_simplifier_arm64 (after)
/// CHECK-NOT: TypeConversion
@@ -306,22 +414,41 @@ public class Main {
assertIntEquals(a + $noinline$charToShort(b), a + (short)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendCharInt2(int, char) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
public static void $opt$validateExtendCharInt2(int a, char b) {
// The conversion to `int` has been optimized away, so there is nothing to merge.
assertIntEquals (a + $noinline$charToInt (b), a + (int)b);
- // There is an environment use for `(long)b`, preventing the merge.
+ // There is an environment use for `(long)b` and the implicit `(long)a`, preventing the merge.
assertLongEquals(a + $noinline$charToLong(b), a + (long)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm (after)
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendCharLong(long, char) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
@@ -332,7 +459,7 @@ public class Main {
// The first two tests have a type conversion.
assertLongEquals(a + $noinline$charToByte (b), a + (byte)b);
assertLongEquals(a + $noinline$charToShort(b), a + (short)b);
- // This test does not because the conversion to `int` is optimized away.
+ // On ARM64 this test does not because the conversion to `int` is optimized away.
assertLongEquals(a + $noinline$charToInt (b), a + (int)b);
}
@@ -342,9 +469,12 @@ public class Main {
$opt$validateExtendCharLong(a, b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt1(int, short) instruction_simplifier_arm64 (after)
/// CHECK-NOT: TypeConversion
@@ -354,21 +484,41 @@ public class Main {
assertIntEquals(a + $noinline$shortToChar (b), a + (char)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm (after)
+ /// CHECK-NOT: DataProcWithShifterOp
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendShortInt2(int, short) instruction_simplifier_arm64 (after)
- /// CHECK-NOT: Arm64DataProcWithShifterOp
- /// CHECK-NOT: Arm64DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
public static void $opt$validateExtendShortInt2(int a, short b) {
// The conversion to `int` has been optimized away, so there is nothing to merge.
assertIntEquals (a + $noinline$shortToInt (b), a + (int)b);
- // There is an environment use for `(long)b`, preventing the merge.
+ // There is an environment use for `(long)b` and the implicit `(long)a`, preventing the merge.
assertLongEquals(a + $noinline$shortToLong (b), a + (long)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm (after)
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendShortLong(long, short) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
@@ -379,7 +529,7 @@ public class Main {
// The first two tests have a type conversion.
assertLongEquals(a + $noinline$shortToByte(b), a + (byte)b);
assertLongEquals(a + $noinline$shortToChar(b), a + (char)b);
- // This test does not because the conversion to `int` is optimized away.
+ // On ARM64 this test does not because the conversion to `int` is optimized away.
assertLongEquals(a + $noinline$shortToInt (b), a + (int)b);
}
@@ -389,11 +539,31 @@ public class Main {
$opt$validateExtendShortLong(a, b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm (after)
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendInt(long, int) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
@@ -411,11 +581,34 @@ public class Main {
assertLongEquals(a + $noinline$intToLong (b), a + (long)b);
}
+ /// CHECK-START-ARM: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ /// CHECK-START-ARM: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm (after)
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK-NOT: TypeConversion
+
/// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateExtendLong(long, long) instruction_simplifier_arm64 (after)
/// CHECK: TypeConversion
@@ -449,40 +642,83 @@ public class Main {
// Each test line below should see one merge.
+ /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+ // Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier.
+
+ /// CHECK-START-ARM: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm (after)
+ /// CHECK-NOT: Shl
+ /// CHECK-NOT: Shr
+ /// CHECK-NOT: UShr
+
/// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
// Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier.
/// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after)
@@ -552,43 +788,89 @@ public class Main {
}
// Each test line below should see one merge.
+ /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
+
+ // On ARM shifts by 1 are not merged.
+ /// CHECK-START-ARM: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm (after)
+ /// CHECK: Shl
+ /// CHECK-NOT: Shl
+ /// CHECK: Shr
+ /// CHECK-NOT: Shr
+ /// CHECK: UShr
+ /// CHECK-NOT: UShr
+
/// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK: DataProcWithShifterOp
+ /// CHECK-NOT: DataProcWithShifterOp
/// CHECK-START-ARM64: void Main.$opt$validateShiftLong(long, long) instruction_simplifier_arm64 (after)
/// CHECK-NOT: Shl
diff --git a/test/593-checker-shift-and-simplifier/src/Main.java b/test/593-checker-shift-and-simplifier/src/Main.java
index 65e809a30e..c9826bc546 100644
--- a/test/593-checker-shift-and-simplifier/src/Main.java
+++ b/test/593-checker-shift-and-simplifier/src/Main.java
@@ -21,6 +21,17 @@ public class Main {
// A very particular set of operations that caused a double removal by the
// ARM64 simplifier doing "forward" removals (b/27851582).
+ /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (before)
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet
+ /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+ /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+ /// CHECK-DAG: And [<<Not>>,<<Shl>>]
+ //
+ /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (after)
+ /// CHECK-DAG: <<Get:i\d+>> ArrayGet
+ /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+ /// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+
/// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (before)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
@@ -30,7 +41,7 @@ public class Main {
/// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: Arm64DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+ /// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
private static int operations() {
int r = a[0];
int n = ~r;
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index 75b782e8c0..63da4f5560 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -232,8 +232,9 @@ public class Main {
/// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
static int bufferDeadLoop() {
StringBuffer b = new StringBuffer();
+ String x = "x";
for (int i = 0; i < 10; i++) {
- int d = b.toString().indexOf("x", 1);
+ int d = b.toString().indexOf(x, 1);
}
return b.length();
}
@@ -252,8 +253,9 @@ public class Main {
/// CHECK-NOT: InvokeVirtual intrinsic:StringStringIndexOfAfter
static int builderDeadLoop() {
StringBuilder b = new StringBuilder();
+ String x = "x";
for (int i = 0; i < 10; i++) {
- int d = b.toString().indexOf("x", 1);
+ int d = b.toString().indexOf(x, 1);
}
return b.length();
}
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index b35dff48ee..e1af02edfd 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -27,7 +27,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_nativeClearResolvedTypes(JNIEnv*, jc
ScopedObjectAccess soa(Thread::Current());
mirror::DexCache* dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
- dex_cache->SetResolvedType(dex::TypeIndex(i), ObjPtr<mirror::Class>(nullptr));
+ mirror::TypeDexCachePair cleared(nullptr, mirror::TypeDexCachePair::InvalidIndexForSlot(i));
+ dex_cache->GetResolvedTypes()[i].store(cleared, std::memory_order_relaxed);
}
}
diff --git a/test/638-no-line-number/build b/test/638-no-line-number/build
new file mode 100644
index 0000000000..7eaf50e938
--- /dev/null
+++ b/test/638-no-line-number/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stop if something fails.
+set -e
+
+mkdir classes
+# Only keep the source name, to make sure we do remove it in the stack trace
+# when there is no line number mapping.
+${JAVAC} -g:source -source 7 -target 7 -d classes `find src -name '*.java'`
+${DX} --dex --output=classes.dex classes
+zip $TEST_NAME.jar classes.dex
diff --git a/test/638-no-line-number/expected.txt b/test/638-no-line-number/expected.txt
new file mode 100644
index 0000000000..ffde15312b
--- /dev/null
+++ b/test/638-no-line-number/expected.txt
@@ -0,0 +1,5 @@
+java.lang.Error
+ at Main.main(Unknown Source:2)
+java.lang.NullPointerException: throw with null exception
+ at Main.doThrow(Unknown Source:0)
+ at Main.main(Unknown Source:9)
diff --git a/test/638-no-line-number/info.txt b/test/638-no-line-number/info.txt
new file mode 100644
index 0000000000..89e6432e66
--- /dev/null
+++ b/test/638-no-line-number/info.txt
@@ -0,0 +1 @@
+Test for b/30183883, that we emit the dex pc when the line number is missing.
diff --git a/test/638-no-line-number/src/Main.java b/test/638-no-line-number/src/Main.java
new file mode 100644
index 0000000000..7fe0404204
--- /dev/null
+++ b/test/638-no-line-number/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ doThrow(new Error());
+ } catch (Error e) {
+ e.printStackTrace();
+ }
+ try {
+ doThrow(null);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public static void doThrow(Error e) {
+ throw e;
+ }
+}
diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc
index 2b50eb83b4..adae844ef0 100644
--- a/test/909-attach-agent/attach.cc
+++ b/test/909-attach-agent/attach.cc
@@ -28,7 +28,7 @@ namespace Test909AttachAgent {
jint OnAttach(JavaVM* vm,
char* options ATTRIBUTE_UNUSED,
void* reserved ATTRIBUTE_UNUSED) {
- printf("Attached Agent for test 909-attach-agent\n");
+ fprintf(stderr, "Attached Agent for test 909-attach-agent\n");
fsync(1);
jvmtiEnv* env = nullptr;
jvmtiEnv* env2 = nullptr;
@@ -36,7 +36,7 @@ jint OnAttach(JavaVM* vm,
#define CHECK_CALL_SUCCESS(c) \
do { \
if ((c) != JNI_OK) { \
- printf("call " #c " did not succeed\n"); \
+ fprintf(stderr, "call " #c " did not succeed\n"); \
return -1; \
} \
} while (false)
@@ -44,7 +44,7 @@ jint OnAttach(JavaVM* vm,
CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env), JVMTI_VERSION_1_0));
CHECK_CALL_SUCCESS(vm->GetEnv(reinterpret_cast<void**>(&env2), JVMTI_VERSION_1_0));
if (env == env2) {
- printf("GetEnv returned same environment twice!\n");
+ fprintf(stderr, "GetEnv returned same environment twice!\n");
return -1;
}
unsigned char* local_data = nullptr;
@@ -54,19 +54,19 @@ jint OnAttach(JavaVM* vm,
unsigned char* get_data = nullptr;
CHECK_CALL_SUCCESS(env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data)));
if (get_data != local_data) {
- printf("Got different data from local storage then what was set!\n");
+ fprintf(stderr, "Got different data from local storage then what was set!\n");
return -1;
}
CHECK_CALL_SUCCESS(env2->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&get_data)));
if (get_data != nullptr) {
- printf("env2 did not have nullptr local storage.\n");
+ fprintf(stderr, "env2 did not have nullptr local storage.\n");
return -1;
}
CHECK_CALL_SUCCESS(env->Deallocate(local_data));
jint version = 0;
CHECK_CALL_SUCCESS(env->GetVersionNumber(&version));
if ((version & JVMTI_VERSION_1) != JVMTI_VERSION_1) {
- printf("Unexpected version number!\n");
+ fprintf(stderr, "Unexpected version number!\n");
return -1;
}
CHECK_CALL_SUCCESS(env->DisposeEnvironment());
diff --git a/test/910-methods/expected.txt b/test/910-methods/expected.txt
index c913b3ffe5..e87929f00c 100644
--- a/test/910-methods/expected.txt
+++ b/test/910-methods/expected.txt
@@ -28,7 +28,7 @@ Location end: JVMTI_ERROR_NATIVE_METHOD
Is native: true
Is obsolete: false
Is synthetic: false
-[add, (Ljava/lang/Object;)Z, null]
+[add, (Ljava/lang/Object;)Z, (TE;)Z]
interface java.util.List
1025
Max locals: 0
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 328216b324..e932b206c0 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -1,10 +1,10 @@
[Ljava/lang/Object;, null]
1
-[Ljava/lang/String;, null]
+[Ljava/lang/String;, Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence;]
11
[Ljava/lang/Math;, null]
11
-[Ljava/util/List;, null]
+[Ljava/util/List;, <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;]
601
[L$Proxy0;, null]
11
diff --git a/test/918-fields/expected.txt b/test/918-fields/expected.txt
index 39d3e70550..1a1209c34c 100644
--- a/test/918-fields/expected.txt
+++ b/test/918-fields/expected.txt
@@ -14,3 +14,7 @@ true
interface Main$Bar
25
false
+[generics, Ljava/lang/Object;, TT;]
+class Main$Generics
+0
+false
diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java
index 3ba535b31b..ad0d0c5dc2 100644
--- a/test/918-fields/src/Main.java
+++ b/test/918-fields/src/Main.java
@@ -27,6 +27,7 @@ public class Main {
testField(Integer.class, "value");
testField(Foo.class, "this$0");
testField(Bar.class, "VAL");
+ testField(Generics.class, "generics");
}
private static void testField(Class<?> base, String fieldName)
@@ -65,4 +66,8 @@ public class Main {
private static interface Bar {
public static int VAL = 1;
}
+
+ private static class Generics<T> {
+ T generics;
+ }
}
diff --git a/test/948-change-annotations/build b/test/948-change-annotations/build
new file mode 100755
index 0000000000..898e2e54a2
--- /dev/null
+++ b/test/948-change-annotations/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/948-change-annotations/expected.txt b/test/948-change-annotations/expected.txt
new file mode 100644
index 0000000000..7974c7a694
--- /dev/null
+++ b/test/948-change-annotations/expected.txt
@@ -0,0 +1,21 @@
+Running test class RemoveAnnotationsTest
+Type annotations: [@TestClassAnnotation1(value=hello)]
+method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi)]
+hello
+Goodbye
+Type annotations: []
+method public void Transform.sayHi() -> []
+Running test class AddAnnotationsTest
+Type annotations: [@TestClassAnnotation1(value=hello)]
+method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi)]
+hello
+Goodbye
+Type annotations: [@TestClassAnnotation1(value=hello), @TestClassAnnotation2(value=hello2)]
+method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi), @TestMethodAnnotation2(value=hi hi2)]
+Running test class ChangeAnnotationValues
+Type annotations: [@TestClassAnnotation1(value=hello)]
+method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=hi hi)]
+hello
+Goodbye
+Type annotations: [@TestClassAnnotation1(value=Goodbye)]
+method public void Transform.sayHi() -> [@TestMethodAnnotation1(value=Bye Bye)]
diff --git a/test/948-change-annotations/info.txt b/test/948-change-annotations/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/948-change-annotations/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/948-change-annotations/run b/test/948-change-annotations/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/948-change-annotations/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/948-change-annotations/src/AddAnnotationsTest.java b/test/948-change-annotations/src/AddAnnotationsTest.java
new file mode 100644
index 0000000000..6876e8736e
--- /dev/null
+++ b/test/948-change-annotations/src/AddAnnotationsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class AddAnnotationsTest implements TestCase {
+ /**
+ * base64 encoded class/dex file for
+ * @TestClassAnnotation1("hello")
+ * @TestClassAnnotation2("hello2")
+ * class Transform {
+ * @TestMethodAnnotation1("hi hi")
+ * @TestMethodAnnotation2("hi hi2")
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAKQoABgAbCQAcAB0IAB4KAB8AIAcAIQcAIgEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTFRyYW5zZm9y" +
+ "bTsBAAVzYXlIaQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBABdMVGVzdE1ldGhvZEFubm90" +
+ "YXRpb24xOwEABXZhbHVlAQAFaGkgaGkBABdMVGVzdE1ldGhvZEFubm90YXRpb24yOwEABmhpIGhp" +
+ "MgEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQEAFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsB" +
+ "AAVoZWxsbwEAFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMjsBAAZoZWxsbzIMAAcACAcAIwwAJAAlAQAH" +
+ "R29vZGJ5ZQcAJgwAJwAoAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFu" +
+ "Zy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3Ry" +
+ "ZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAAAAAAAgAAAAcACAAB" +
+ "AAkAAAAvAAEAAQAAAAUqtwABsQAAAAIACgAAAAYAAQAAABMACwAAAAwAAQAAAAUADAANAAAAAQAO" +
+ "AAgAAgAJAAAANwACAAEAAAAJsgACEgO2AASxAAAAAgAKAAAACgACAAAAFwAIABgACwAAAAwAAQAA" +
+ "AAkADAANAAAADwAAABQAAgAQAAEAEXMAEgATAAEAEXMAFAACABUAAAACABYADwAAABQAAgAXAAEA" +
+ "EXMAGAAZAAEAEXMAGg==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQA7mPKPjUKe43s+OLHHgFVRVCAPn/rRz9z0AwAAcAAAAHhWNBIAAAAAAAAAADADAAAX" +
+ "AAAAcAAAAAoAAADMAAAAAgAAAPQAAAABAAAADAEAAAQAAAAUAQAAAQAAADQBAACgAgAAVAEAAMYB" +
+ "AADOAQAA1wEAAO8BAAAHAgAAIAIAADkCAABGAgAAXQIAAHECAACFAgAAmQIAAKkCAACsAgAAsAIA" +
+ "AMQCAADLAgAA0wIAANoCAADiAgAA5wIAAPACAAD3AgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAA" +
+ "CAAAAAkAAAAKAAAADAAAAAwAAAAJAAAAAAAAAA0AAAAJAAAAwAEAAAgABQATAAAABAAAAAAAAAAE" +
+ "AAAAFQAAAAUAAQAUAAAABgAAAAAAAAAEAAAAAAAAAAYAAAAAAAAACwAAAKgBAAAhAwAAAAAAAAIA" +
+ "AAAJAwAADwMAAAIAAAAVAwAAGwMAAAEAAQABAAAA/gIAAAQAAABwEAMAAAAOAAMAAQACAAAAAwMA" +
+ "AAkAAABiAAAAGwEBAAAAbiACABAADgAAAFQBAAAAAAAAAQAAAAAAAAABAAAAYAEAAAEAAAAHAAY8" +
+ "aW5pdD4AB0dvb2RieWUAFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsAFkxUZXN0Q2xhc3NBbm5vdGF0" +
+ "aW9uMjsAF0xUZXN0TWV0aG9kQW5ub3RhdGlvbjE7ABdMVGVzdE1ldGhvZEFubm90YXRpb24yOwAL" +
+ "TFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJM" +
+ "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYA" +
+ "AlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAAZoZWxsbzIABWhpIGhpAAZoaSBoaTIAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkABXZhbHVlABMABw4AFwAHDocAAQABFhcPAQEBFhcQAQIBFhcRAQMB" +
+ "FhcSAAABAQCAgATsAgEBhAMAEAAAAAAAAAABAAAAAAAAAAEAAAAXAAAAcAAAAAIAAAAKAAAAzAAA" +
+ "AAMAAAACAAAA9AAAAAQAAAABAAAADAEAAAUAAAAEAAAAFAEAAAYAAAABAAAANAEAAAMQAAACAAAA" +
+ "VAEAAAEgAAACAAAAbAEAAAYgAAABAAAAqAEAAAEQAAABAAAAwAEAAAIgAAAXAAAAxgEAAAMgAAAC" +
+ "AAAA/gIAAAQgAAAEAAAACQMAAAAgAAABAAAAIQMAAAAQAAABAAAAMAMAAA==");
+
+ public void runTest(Transform t) {
+ t.sayHi();
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ t.sayHi();
+ }
+}
diff --git a/test/948-change-annotations/src/ChangeAnnotationValues.java b/test/948-change-annotations/src/ChangeAnnotationValues.java
new file mode 100644
index 0000000000..89a766cdeb
--- /dev/null
+++ b/test/948-change-annotations/src/ChangeAnnotationValues.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class ChangeAnnotationValues implements TestCase {
+ /**
+ * base64 encoded class/dex file for
+ * @TestClassAnnotation1("Goodbye")
+ * class Transform {
+ * @TestMethodAnnotation1("Bye Bye")
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAJAoABgAXCQAYABkIABYKABoAGwcAHAcAHQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTFRyYW5zZm9y" +
+ "bTsBAAVzYXlIaQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBABdMVGVzdE1ldGhvZEFubm90" +
+ "YXRpb24xOwEABXZhbHVlAQAHQnllIEJ5ZQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQEA" +
+ "FkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsBAAdHb29kYnllDAAHAAgHAB4MAB8AIAcAIQwAIgAjAQAJ" +
+ "VHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVM" +
+ "amF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShM" +
+ "amF2YS9sYW5nL1N0cmluZzspVgAgAAUABgAAAAAAAgAAAAcACAABAAkAAAAvAAEAAQAAAAUqtwAB" +
+ "sQAAAAIACgAAAAYAAQAAAAIACwAAAAwAAQAAAAUADAANAAAAAQAOAAgAAgAJAAAANwACAAEAAAAJ" +
+ "sgACEgO2AASxAAAAAgAKAAAACgACAAAABQAIAAYACwAAAAwAAQAAAAkADAANAAAADwAAAAsAAQAQ" +
+ "AAEAEXMAEgACABMAAAACABQADwAAAAsAAQAVAAEAEXMAFg==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAXfYs9FUE830lxfnB+X66S7iZiP5A7uDSAAwAAcAAAAHhWNBIAAAAAAAAAALwCAAAS" +
+ "AAAAcAAAAAgAAAC4AAAAAgAAANgAAAABAAAA8AAAAAQAAAD4AAAAAQAAABgBAABIAgAAOAEAAKIB" +
+ "AACqAQAAswEAALwBAADUAQAA7QEAAPoBAAARAgAAJQIAADkCAABNAgAAXQIAAGACAABkAgAAeAIA" +
+ "AH0CAACGAgAAjQIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAALAAAACwAAAAcAAAAAAAAA" +
+ "DAAAAAcAAACcAQAABgADAA4AAAACAAAAAAAAAAIAAAAQAAAAAwABAA8AAAAEAAAAAAAAAAIAAAAA" +
+ "AAAABAAAAAAAAAAKAAAAhAEAAKsCAAAAAAAAAQAAAJ8CAAABAAAApQIAAAEAAQABAAAAlAIAAAQA" +
+ "AABwEAMAAAAOAAMAAQACAAAAmQIAAAkAAABiAAAAGwECAAAAbiACABAADgAAADgBAAAAAAAAAQAA" +
+ "AAAAAAABAAAAQAEAAAEAAAAFAAY8aW5pdD4AB0J5ZSBCeWUAB0dvb2RieWUAFkxUZXN0Q2xhc3NB" +
+ "bm5vdGF0aW9uMTsAF0xUZXN0TWV0aG9kQW5ub3RhdGlvbjE7AAtMVHJhbnNmb3JtOwAVTGphdmEv" +
+ "aW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAS" +
+ "TGphdmEvbGFuZy9TeXN0ZW07AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2st" +
+ "NC4yNQADb3V0AAdwcmludGxuAAVzYXlIaQAFdmFsdWUAAgAHDgAFAAcOhwABAAERFwIBAQERFwEA" +
+ "AAEBAICABMgCAQHgAgAAABAAAAAAAAAAAQAAAAAAAAABAAAAEgAAAHAAAAACAAAACAAAALgAAAAD" +
+ "AAAAAgAAANgAAAAEAAAAAQAAAPAAAAAFAAAABAAAAPgAAAAGAAAAAQAAABgBAAADEAAAAgAAADgB" +
+ "AAABIAAAAgAAAEgBAAAGIAAAAQAAAIQBAAABEAAAAQAAAJwBAAACIAAAEgAAAKIBAAADIAAAAgAA" +
+ "AJQCAAAEIAAAAgAAAJ8CAAAAIAAAAQAAAKsCAAAAEAAAAQAAALwCAAA=");
+
+ public void runTest(Transform t) {
+ t.sayHi();
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ t.sayHi();
+ }
+}
diff --git a/test/948-change-annotations/src/Main.java b/test/948-change-annotations/src/Main.java
new file mode 100644
index 0000000000..30bfbf9fc6
--- /dev/null
+++ b/test/948-change-annotations/src/Main.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Comparator;
+import java.lang.reflect.*;
+import java.lang.annotation.*;
+public class Main {
+
+ /**
+ * base64 encoded class/dex file for for initial Transform.java
+ */
+ private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAJAoABgAXCQAYABkIABYKABoAGwcAHAcAHQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQALTFRyYW5zZm9y" +
+ "bTsBAAVzYXlIaQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBABdMVGVzdE1ldGhvZEFubm90" +
+ "YXRpb24xOwEABXZhbHVlAQAFaGkgaGkBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEBABZM" +
+ "VGVzdENsYXNzQW5ub3RhdGlvbjE7AQAFaGVsbG8MAAcACAcAHgwAHwAgBwAhDAAiACMBAAlUcmFu" +
+ "c2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZh" +
+ "L2xhbmcvU3RyaW5nOylWACAABQAGAAAAAAACAAAABwAIAAEACQAAAC8AAQABAAAABSq3AAGxAAAA" +
+ "AgAKAAAABgABAAAAEgALAAAADAABAAAABQAMAA0AAAABAA4ACAACAAkAAAA3AAIAAQAAAAmyAAIS" +
+ "A7YABLEAAAACAAoAAAAKAAIAAAAVAAgAFgALAAAADAABAAAACQAMAA0AAAAPAAAACwABABAAAQAR" +
+ "cwASAAIAEwAAAAIAFAAPAAAACwABABUAAQARcwAW");
+ private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCufKz9atC18kWgSsEfRq699UEcX4cHonN8AwAAcAAAAHhWNBIAAAAAAAAAALgCAAAS" +
+ "AAAAcAAAAAgAAAC4AAAAAgAAANgAAAABAAAA8AAAAAQAAAD4AAAAAQAAABgBAABEAgAAOAEAAKIB" +
+ "AACqAQAAwgEAANsBAADoAQAA/wEAABMCAAAnAgAAOwIAAEsCAABOAgAAUgIAAGYCAABtAgAAdAIA" +
+ "AHkCAACCAgAAiQIAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAcAAAAAAAAA" +
+ "CgAAAAcAAACcAQAABgADAA4AAAACAAAAAAAAAAIAAAAQAAAAAwABAA8AAAAEAAAAAAAAAAIAAAAA" +
+ "AAAABAAAAAAAAAAIAAAAhAEAAKcCAAAAAAAAAQAAAJsCAAABAAAAoQIAAAEAAQABAAAAkAIAAAQA" +
+ "AABwEAMAAAAOAAMAAQACAAAAlQIAAAkAAABiAAAAGwEMAAAAbiACABAADgAAADgBAAAAAAAAAQAA" +
+ "AAAAAAABAAAAQAEAAAEAAAAFAAY8aW5pdD4AFkxUZXN0Q2xhc3NBbm5vdGF0aW9uMTsAF0xUZXN0" +
+ "TWV0aG9kQW5ub3RhdGlvbjE7AAtMVHJhbnNmb3JtOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJM" +
+ "amF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07" +
+ "AA5UcmFuc2Zvcm0uamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4yNQAFaGVsbG8ABWhpIGhp" +
+ "AANvdXQAB3ByaW50bG4ABXNheUhpAAV2YWx1ZQASAAcOABUABw6HAAEAAREXDAEBAREXDQAAAQEA" +
+ "gIAEyAIBAeACAAAAEAAAAAAAAAABAAAAAAAAAAEAAAASAAAAcAAAAAIAAAAIAAAAuAAAAAMAAAAC" +
+ "AAAA2AAAAAQAAAABAAAA8AAAAAUAAAAEAAAA+AAAAAYAAAABAAAAGAEAAAMQAAACAAAAOAEAAAEg" +
+ "AAACAAAASAEAAAYgAAABAAAAhAEAAAEQAAABAAAAnAEAAAIgAAASAAAAogEAAAMgAAACAAAAkAIA" +
+ "AAQgAAACAAAAmwIAAAAgAAABAAAApwIAAAAQAAABAAAAuAIAAA==");
+
+ public static void main(String[] args) {
+ doTest(new RemoveAnnotationsTest());
+ doTest(new AddAnnotationsTest());
+ doTest(new ChangeAnnotationValues());
+ }
+
+ private static Annotation[] sortedAnno(Annotation[] annos) {
+ Arrays.sort(annos, Comparator.comparing((v) -> v.toString()));
+ return annos;
+ }
+
+ public static void doTest(TestCase t) {
+ // Get back to normal first.
+ doCommonClassRedefinition(Transform.class, INITIAL_CLASS_BYTES, INITIAL_DEX_BYTES);
+ System.out.println("Running test " + t.getClass());
+ printAnnotations(Transform.class);
+ t.runTest(new Transform());
+ printAnnotations(Transform.class);
+ }
+
+ private static void printAnnotations(Class<?> transform) {
+ System.out.println("Type annotations: "
+ + Arrays.toString(sortedAnno(transform.getAnnotations())));
+ for (Method m : transform.getDeclaredMethods()) {
+ System.out.println("method " + m + " -> "
+ + Arrays.toString(sortedAnno(m.getDeclaredAnnotations())));
+ }
+ }
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}
diff --git a/test/948-change-annotations/src/RemoveAnnotationsTest.java b/test/948-change-annotations/src/RemoveAnnotationsTest.java
new file mode 100644
index 0000000000..3b1725a67e
--- /dev/null
+++ b/test/948-change-annotations/src/RemoveAnnotationsTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class RemoveAnnotationsTest implements TestCase {
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+ "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+ "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABQAG" +
+ "AAAAAAACAAAABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+ "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAATAAgAFAABAAwAAAACAA0=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+ public void runTest(Transform t) {
+ t.sayHi();
+ Main.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ t.sayHi();
+ }
+}
diff --git a/test/948-change-annotations/src/TestCase.java b/test/948-change-annotations/src/TestCase.java
new file mode 100644
index 0000000000..9edc01e4f9
--- /dev/null
+++ b/test/948-change-annotations/src/TestCase.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface TestCase {
+ public void runTest(Transform t);
+}
diff --git a/test/948-change-annotations/src/TestClassAnnotation1.java b/test/948-change-annotations/src/TestClassAnnotation1.java
new file mode 100644
index 0000000000..adef98f5d4
--- /dev/null
+++ b/test/948-change-annotations/src/TestClassAnnotation1.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.*;
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @ interface TestClassAnnotation1 {
+ public String value();
+}
diff --git a/test/948-change-annotations/src/TestClassAnnotation2.java b/test/948-change-annotations/src/TestClassAnnotation2.java
new file mode 100644
index 0000000000..67e6260459
--- /dev/null
+++ b/test/948-change-annotations/src/TestClassAnnotation2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.*;
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @ interface TestClassAnnotation2 {
+ public String value();
+}
diff --git a/test/948-change-annotations/src/TestMethodAnnotation1.java b/test/948-change-annotations/src/TestMethodAnnotation1.java
new file mode 100644
index 0000000000..d3920f3976
--- /dev/null
+++ b/test/948-change-annotations/src/TestMethodAnnotation1.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.*;
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @ interface TestMethodAnnotation1 {
+ public String value();
+}
diff --git a/test/948-change-annotations/src/TestMethodAnnotation2.java b/test/948-change-annotations/src/TestMethodAnnotation2.java
new file mode 100644
index 0000000000..2d5bb728a4
--- /dev/null
+++ b/test/948-change-annotations/src/TestMethodAnnotation2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.*;
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @ interface TestMethodAnnotation2 {
+ public String value();
+}
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h b/test/948-change-annotations/src/Transform.java
index f73d07a618..1c6a145da4 100644
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.h
+++ b/test/948-change-annotations/src/Transform.java
@@ -14,15 +14,10 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
-#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
-
-#include <jni.h>
-
-namespace art {
-
-void register_dalvik_system_InMemoryDexClassLoader_DexData(JNIEnv* env);
-
-} // namespace art
-
-#endif // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_INMEMORYDEXCLASSLOADER_DEXDATA_H_
+@TestClassAnnotation1("hello")
+class Transform {
+ @TestMethodAnnotation1("hi hi")
+ public void sayHi() {
+ System.out.println("hello");
+ }
+}
diff --git a/test/949-in-memory-transform/expected.txt b/test/949-in-memory-transform/expected.txt
new file mode 100644
index 0000000000..4774b81b49
--- /dev/null
+++ b/test/949-in-memory-transform/expected.txt
@@ -0,0 +1,2 @@
+hello
+Goodbye
diff --git a/test/949-in-memory-transform/info.txt b/test/949-in-memory-transform/info.txt
new file mode 100644
index 0000000000..7753729dc7
--- /dev/null
+++ b/test/949-in-memory-transform/info.txt
@@ -0,0 +1,4 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that transformation works even when the class being transformed comes from
+an in-memory dex file (i.e. loaded from an InMemoryDexClassLoader).
diff --git a/test/949-in-memory-transform/run b/test/949-in-memory-transform/run
new file mode 100755
index 0000000000..e92b873956
--- /dev/null
+++ b/test/949-in-memory-transform/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/949-in-memory-transform/src/Main.java b/test/949-in-memory-transform/src/Main.java
new file mode 100644
index 0000000000..2ffabf5424
--- /dev/null
+++ b/test/949-in-memory-transform/src/Main.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+import java.lang.reflect.*;
+import java.nio.ByteBuffer;
+
+public class Main {
+ /**
+ * base64 encoded class/dex file for
+ * public class Transform {
+ * public void sayHi() {
+ * System.out.println("hello");
+ * }
+ * }
+ */
+ private static final byte[] INITIAL_CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAVoZWxsbwcAGQwAGgAbAQAJVHJhbnNmb3JtAQAQamF2YS9sYW5nL09iamVj" +
+ "dAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZh" +
+ "L2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAA" +
+ "AAAAAgABAAcACAABAAkAAAAdAAEAAQAAAAUqtwABsQAAAAEACgAAAAYAAQAAABEAAQALAAgAAQAJ" +
+ "AAAAJQACAAEAAAAJsgACEgO2AASxAAAAAQAKAAAACgACAAAAGgAIABsAAQAMAAAAAgAN");
+ private static final byte[] INITIAL_DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAJX3mZphwHJCT1qdTz/GS+jXOR+O/9e3fMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAdwEAAI4BAACiAQAAtgEAAMoBAADaAQAA3QEAAOEBAAD1AQAA/AEAAAECAAAKAgAAAQAA" +
+ "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAGAAAAAAAAABwCAAAA" +
+ "AAAAAQABAAEAAAARAgAABAAAAHAQAwAAAA4AAwABAAIAAAAWAgAACQAAAGIAAAAbAQoAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwAS" +
+ "TGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVt" +
+ "OwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUABWhlbGxvAANvdXQA" +
+ "B3ByaW50bG4ABXNheUhpABEABw4AGgAHDocAAAABAQCBgASgAgEBuAIAAA0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABECAAAAIAAAAQAAABwCAAAAEAAAAQAAACwCAAA=");
+
+
+ /**
+ * base64 encoded class/dex file for
+ * public class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] TRANSFORMED_CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAHAoABgAOCQAPABAIABEKABIAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUB" +
+ "AA9MaW5lTnVtYmVyVGFibGUBAAVzYXlIaQEAClNvdXJjZUZpbGUBAA5UcmFuc2Zvcm0uamF2YQwA" +
+ "BwAIBwAWDAAXABgBAAdHb29kYnllBwAZDAAaABsBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2Jq" +
+ "ZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2ph" +
+ "dmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAG" +
+ "AAAAAAACAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAEQABAAsACAAB" +
+ "AAkAAAAlAAIAAQAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAaAAgAGwABAAwAAAACAA0=");
+ private static final byte[] TRANSFORMED_DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAPXh6T3l1FObhHsKf1U2vi+0GmAvElxBLMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAABAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMjUAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgAaAAcOhwAAAAEBAIGABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+
+ public static void main(String[] args) throws Exception {
+ ClassLoader loader;
+ try {
+ // Art uses this classloader to do in-memory dex files. There is no support for defineClass
+ loader = (ClassLoader)Class.forName("dalvik.system.InMemoryDexClassLoader")
+ .getConstructor(ByteBuffer.class, ClassLoader.class)
+ .newInstance(ByteBuffer.wrap(INITIAL_DEX_BYTES),
+ ClassLoader.getSystemClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Seem to be on RI. Just make a new ClassLoader that calls defineClass.
+ loader = new ClassLoader() {
+ public Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("Transform")) {
+ return defineClass(name, INITIAL_CLASS_BYTES, 0, INITIAL_CLASS_BYTES.length);
+ } else {
+ throw new ClassNotFoundException("Couldn't find class: " + name);
+ }
+ }
+ };
+ }
+ doTest(loader);
+ }
+
+ public static void doTest(ClassLoader loader) throws Exception {
+ // Get the class
+ Class<?> transform_class = loader.loadClass("Transform");
+ Method say_hi_method = transform_class.getMethod("sayHi");
+ Object t = transform_class.newInstance();
+
+ // Run the actual test.
+ say_hi_method.invoke(t);
+ doCommonClassRedefinition(transform_class, TRANSFORMED_CLASS_BYTES, TRANSFORMED_DEX_BYTES);
+ say_hi_method.invoke(t);
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index fc9f030559..cb06e4263e 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -183,15 +183,23 @@ public class Main {
public String bar();
}
- public static class BarSuper {
+ public static abstract class BarAbstractSuper {
+ public abstract String abstractSuperPublicMethod();
+ }
+
+ public static class BarSuper extends BarAbstractSuper {
public String superPublicMethod() {
return "superPublicMethod";
}
- public String superProtectedMethod() {
+ protected String superProtectedMethod() {
return "superProtectedMethod";
}
+ public String abstractSuperPublicMethod() {
+ return "abstractSuperPublicMethod";
+ }
+
String superPackageMethod() {
return "superPackageMethod";
}
@@ -288,15 +296,19 @@ public class Main {
System.out.println("Unexpected return value for BarImpl#bar: " + str);
}
- // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
- // invoke resulting in a failing check in the interpreter.
- //
- // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
- // MethodType.methodType(String.class));
- // str = (String) mh.invoke(new BarImpl());
- // if (!"bar".equals(str)) {
- // System.out.println("Unexpected return value for BarImpl#bar: " + str);
- // }
+ mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"bar".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#bar: " + str);
+ }
+
+ mh = MethodHandles.lookup().findVirtual(BarAbstractSuper.class, "abstractSuperPublicMethod",
+ MethodType.methodType(String.class));
+ str = (String) mh.invoke(new BarImpl());
+ if (!"abstractSuperPublicMethod".equals(str)) {
+ System.out.println("Unexpected return value for BarImpl#abstractSuperPublicMethod: " + str);
+ }
// We should also be able to lookup public / protected / package methods in
// the super class, given sufficient access privileges.
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 1938b92db8..95967b527c 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -170,12 +170,6 @@ endif
ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true)
IMAGE_TYPES := multipicimage
endif
-ifeq ($(ART_TEST_NPIC_IMAGE),true)
- IMAGE_TYPES += npicimage
- ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true)
- IMAGE_TYPES := multinpicimage
- endif
-endif
PICTEST_TYPES := npictest
ifeq ($(ART_TEST_PIC_TEST),true)
PICTEST_TYPES += pictest
@@ -878,32 +872,36 @@ define core-image-dependencies
endif
endif
ifeq ($(2),no-image)
- $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_pic_$(4))
+ $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_$(4))
else
- ifeq ($(2),npicimage)
- $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_no-pic_$(4))
+ ifeq ($(2),picimage)
+ $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_$(4))
else
- ifeq ($(2),picimage)
- $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_pic_$(4))
- else
- ifeq ($(2),multinpicimage)
- $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(4))
- else
- ifeq ($(2),multipicimage)
- $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_pic_multi_$(4))
- endif
- endif
+ ifeq ($(2),multipicimage)
+ $(1)_prereq_rules += $$($(call name-to-var,$(1))_CORE_IMAGE_$$(image_suffix)_multi_$(4))
endif
endif
endif
endef
+COMPILER_TYPES_2 := optimizing
+COMPILER_TYPES_2 += interpreter
+COMPILER_TYPES_2 += jit
+COMPILER_TYPES_2 += regalloc_gc
+COMPILER_TYPES_2 += interp-ac
+ALL_ADDRESS_SIZES_2 := 32 64
+IMAGE_TYPES_2 := picimage
+IMAGE_TYPES_2 += no-image
+IMAGE_TYPES_2 += npicimage
+IMAGE_TYPES_2 += multinpicimage
+IMAGE_TYPES_2 += multipicimage
+
# Add core image dependencies required for given target - HOST or TARGET,
# IMAGE_TYPE, COMPILER_TYPE and ADDRESS_SIZE to the prereq_rules.
$(foreach target, $(TARGET_TYPES), \
- $(foreach image, $(IMAGE_TYPES), \
- $(foreach compiler, $(COMPILER_TYPES), \
- $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \
+ $(foreach image, $(IMAGE_TYPES_2), \
+ $(foreach compiler, $(COMPILER_TYPES_2), \
+ $(foreach address_size, $(ALL_ADDRESS_SIZES_2), $(eval \
$(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size)))))))
test-art-host-run-test-dependencies : $(host_prereq_rules)
@@ -1081,50 +1079,29 @@ define define-test-art-run-test
# Add the core dependency. This is required for pre-building.
# Use the PIC image, as it is the default in run-test, to match dependencies.
ifeq ($(1),host)
- prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_$(13))
+ prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_$(13))
else
- prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13))
+ prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_$(13))
endif
else
- ifeq ($(9),npicimage)
- test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES
- run_test_options += --npic-image
- # Add the core dependency.
+ ifeq ($(9),picimage)
+ test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES
ifeq ($(1),host)
- prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_$(13))
+ prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_$(13))
else
- prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_$(13))
+ prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_$(13))
endif
else
- ifeq ($(9),picimage)
+ ifeq ($(9),multipicimage)
test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES
+ run_test_options += --multi-image
ifeq ($(1),host)
- prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_$(13))
+ prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_multi_$(13))
else
- prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13))
+ prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_multi_$(13))
endif
else
- ifeq ($(9),multinpicimage)
- test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES
- run_test_options += --npic-image --multi-image
- ifeq ($(1),host)
- prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13))
- else
- prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13))
- endif
- else
- ifeq ($(9),multipicimage)
- test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES
- run_test_options += --multi-image
- ifeq ($(1),host)
- prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13))
- else
- prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13))
- endif
- else
- $$(error found $(9) expected $(IMAGE_TYPES))
- endif
- endif
+ $$(error found $(9) expected $(IMAGE_TYPES))
endif
endif
endif
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 7d218f1d61..f3d4332009 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -19,7 +19,7 @@ DEBUGGER="n"
DEV_MODE="n"
DEX2OAT=""
EXPERIMENTAL=""
-FALSE_BIN="/system/bin/false"
+FALSE_BIN="false"
FLAGS=""
ANDROID_FLAGS=""
GDB=""
diff --git a/test/run-test b/test/run-test
index c926c115e2..e808deef52 100755
--- a/test/run-test
+++ b/test/run-test
@@ -143,7 +143,6 @@ never_clean="no"
have_dex2oat="yes"
have_patchoat="yes"
have_image="yes"
-pic_image_suffix=""
multi_image_suffix=""
android_root="/system"
bisection_search="no"
@@ -199,9 +198,6 @@ while true; do
elif [ "x$1" = "x--no-image" ]; then
have_image="no"
shift
- elif [ "x$1" = "x--npic-image" ]; then
- pic_image_suffix="-npic"
- shift
elif [ "x$1" = "x--multi-image" ]; then
multi_image_suffix="-multi"
shift
@@ -507,12 +503,12 @@ if [ "$runtime" = "dalvik" ]; then
elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
guess_host_arch_name
- run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
+ run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${multi_image_suffix}.art"
run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
else
guess_target_arch_name
run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}"
- run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art"
+ run_args="${run_args} --boot /data/art-test/core${image_suffix}${multi_image_suffix}.art"
fi
if [ "$relocate" = "yes" ]; then
run_args="${run_args} --relocate"
@@ -668,8 +664,6 @@ if [ "$usage" = "yes" ]; then
echo " --dex2oat-swap Use a dex2oat swap file."
echo " --instruction-set-features [string]"
echo " Set instruction-set-features for compilation."
- echo " --npic-image Use an image compiled with non-position independent code "
- echo " for the boot class path."
echo " --multi-image Use a set of images compiled with dex2oat multi-image for"
echo " the boot class path."
echo " --pic-test Compile the test code position independent."
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 0b69718f93..f327974dae 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -105,9 +105,6 @@ ART_TEST_OPTIMIZING = getEnvBoolean('ART_TEST_OPTIMIZING', ART_TEST_FULL)
# Do you want to test the optimizing compiler with graph coloring register allocation?
ART_TEST_OPTIMIZING_GRAPH_COLOR = getEnvBoolean('ART_TEST_OPTIMIZING_GRAPH_COLOR', ART_TEST_FULL)
-# Do we want to test a non-PIC-compiled core image?
-ART_TEST_NPIC_IMAGE = getEnvBoolean('ART_TEST_NPIC_IMAGE', ART_TEST_FULL)
-
# Do we want to test PIC-compiled tests ("apps")?
ART_TEST_PIC_TEST = getEnvBoolean('ART_TEST_PIC_TEST', ART_TEST_FULL)
# Do you want tracing tests run?
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index a5bfcffae1..748ec31ae4 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -133,8 +133,7 @@ def gather_test_info():
VARIANT_TYPE_DICT['run'] = {'ndebug', 'debug'}
VARIANT_TYPE_DICT['target'] = {'target', 'host'}
VARIANT_TYPE_DICT['trace'] = {'trace', 'ntrace', 'stream'}
- VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'npicimage',
- 'multinpicimage', 'multipicimage'}
+ VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'multipicimage'}
VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'}
VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'}
VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'no-dex2oat', 'prebuild'}
@@ -218,10 +217,6 @@ def setup_test_env():
IMAGE_TYPES.add('no-image')
if env.ART_TEST_RUN_TEST_MULTI_IMAGE:
IMAGE_TYPES.add('multipicimage')
- if env.ART_TEST_NPIC_IMAGE:
- IMAGE_TYPES.add('npicimage')
- if env.ART_TEST_RUN_TEST_MULTI_IMAGE:
- IMAGE_TYPES.add('multinpicimage')
if env.ART_TEST_RUN_TEST_IMAGE or not IMAGE_TYPES: # Default
IMAGE_TYPES.add('picimage')
@@ -388,10 +383,6 @@ def run_tests(tests):
if image == 'no-image':
options_test += ' --no-image'
- elif image == 'npicimage':
- options_test += ' --npic-image'
- elif image == 'multinpicimage':
- options_test += ' --npic-image --multi-image'
elif image == 'multipicimage':
options_test += ' --multi-image'
@@ -446,29 +437,34 @@ def run_test(command, test, test_variant, test_name):
test_name: The name of the test along with the variants.
"""
global stop_testrunner
- if is_test_disabled(test, test_variant):
- test_skipped = True
- else:
- test_skipped = False
- proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
- script_output = proc.stdout.read().strip()
- test_passed = not proc.wait()
-
- if not test_skipped:
- if test_passed:
- print_test_info(test_name, 'PASS')
+ try:
+ if is_test_disabled(test, test_variant):
+ test_skipped = True
else:
- failed_tests.append(test_name)
- if not env.ART_TEST_KEEP_GOING:
- stop_testrunner = True
- print_test_info(test_name, 'FAIL', ('%s\n%s') % (
- command, script_output))
- elif not dry_run:
- print_test_info(test_name, 'SKIP')
- skipped_tests.append(test_name)
- else:
- print_test_info(test_name, '')
- semaphore.release()
+ test_skipped = False
+ proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
+ script_output = proc.stdout.read().strip()
+ test_passed = not proc.wait()
+
+ if not test_skipped:
+ if test_passed:
+ print_test_info(test_name, 'PASS')
+ else:
+ failed_tests.append(test_name)
+ if not env.ART_TEST_KEEP_GOING:
+ stop_testrunner = True
+ print_test_info(test_name, 'FAIL', ('%s\n%s') % (
+ command, script_output))
+ elif not dry_run:
+ print_test_info(test_name, 'SKIP')
+ skipped_tests.append(test_name)
+ else:
+ print_test_info(test_name, '')
+ except Exception, e:
+ failed_tests.append(test_name)
+ print_text(('%s\n%s\n') % (command, str(e)))
+ finally:
+ semaphore.release()
def print_test_info(test_name, result, failed_test_info=""):
@@ -485,6 +481,7 @@ def print_test_info(test_name, result, failed_test_info=""):
command used to invoke the script. It doesn't override the failing
test information in either of the cases.
"""
+
global test_count
info = ''
if not verbose:
@@ -493,48 +490,53 @@ def print_test_info(test_name, result, failed_test_info=""):
# the console width.
console_width = int(os.popen('stty size', 'r').read().split()[1])
info = '\r' + ' ' * console_width + '\r'
- print_mutex.acquire()
- test_count += 1
- percent = (test_count * 100) / total_test_count
- progress_info = ('[ %d%% %d/%d ]') % (
- percent,
- test_count,
- total_test_count)
-
- if result == "FAIL":
- info += ('%s %s %s\n%s\n') % (
- progress_info,
- test_name,
- COLOR_ERROR + 'FAIL' + COLOR_NORMAL,
- failed_test_info)
- else:
- result_text = ''
- if result == 'PASS':
- result_text += COLOR_PASS + 'PASS' + COLOR_NORMAL
- elif result == 'SKIP':
- result_text += COLOR_SKIP + 'SKIP' + COLOR_NORMAL
-
- if verbose:
- info += ('%s %s %s\n') % (
- progress_info,
- test_name,
- result_text)
- else:
- total_output_length = 2 # Two spaces
- total_output_length += len(progress_info)
- total_output_length += len(result)
- allowed_test_length = console_width - total_output_length
- test_name_len = len(test_name)
- if allowed_test_length < test_name_len:
- test_name = ('%s...%s') % (
- test_name[:(allowed_test_length - 3)/2],
- test_name[-(allowed_test_length - 3)/2:])
- info += ('%s %s %s') % (
+ try:
+ print_mutex.acquire()
+ test_count += 1
+ percent = (test_count * 100) / total_test_count
+ progress_info = ('[ %d%% %d/%d ]') % (
+ percent,
+ test_count,
+ total_test_count)
+
+ if result == "FAIL":
+ info += ('%s %s %s\n%s\n') % (
progress_info,
test_name,
- result_text)
- print_text(info)
- print_mutex.release()
+ COLOR_ERROR + 'FAIL' + COLOR_NORMAL,
+ failed_test_info)
+ else:
+ result_text = ''
+ if result == 'PASS':
+ result_text += COLOR_PASS + 'PASS' + COLOR_NORMAL
+ elif result == 'SKIP':
+ result_text += COLOR_SKIP + 'SKIP' + COLOR_NORMAL
+
+ if verbose:
+ info += ('%s %s %s\n') % (
+ progress_info,
+ test_name,
+ result_text)
+ else:
+ total_output_length = 2 # Two spaces
+ total_output_length += len(progress_info)
+ total_output_length += len(result)
+ allowed_test_length = console_width - total_output_length
+ test_name_len = len(test_name)
+ if allowed_test_length < test_name_len:
+ test_name = ('%s...%s') % (
+ test_name[:(allowed_test_length - 3)/2],
+ test_name[-(allowed_test_length - 3)/2:])
+ info += ('%s %s %s') % (
+ progress_info,
+ test_name,
+ result_text)
+ print_text(info)
+ except Exception, e:
+ print_text(('%s\n%s\n') % (test_name, str(e)))
+ failed_tests.append(test_name)
+ finally:
+ print_mutex.release()
def get_disabled_test_info():
"""Generate set of known failures.
@@ -788,10 +790,6 @@ def parse_option():
TRACE_TYPES.add('ntrace')
if options.cms:
GC_TYPES.add('cms')
- if options.npicimage:
- IMAGE_TYPES.add('npicimage')
- if options.multinpicimage:
- IMAGE_TYPES.add('multinpicimage')
if options.multipicimage:
IMAGE_TYPES.add('multipicimage')
if options.verbose:
@@ -837,15 +835,13 @@ def main():
while threading.active_count() > 1:
time.sleep(0.1)
print_analysis()
- if failed_tests:
- sys.exit(1)
- sys.exit(0)
- except SystemExit:
- pass
except Exception, e:
print_analysis()
print_text(str(e))
sys.exit(1)
+ if failed_tests:
+ sys.exit(1)
+ sys.exit(0)
if __name__ == '__main__':
main()
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index dcef8c01c2..08abdb3713 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -105,12 +105,6 @@
names: ["org.apache.harmony.tests.java.lang.ProcessTest#test_getErrorStream"]
},
{
- description: "Short date format flag ignored for es_US locale.",
- result: EXEC_FAILED,
- name: "libcore.icu.DateIntervalFormatTest#test_formatDateInterval",
- bug: 18619426
-},
-{
description: "Error decoding digital signature bytes.",
result: EXEC_FAILED,
name: "org.apache.harmony.security.tests.java.security.Signature2Test#test_verify$BII",
@@ -134,12 +128,6 @@
names: ["org.apache.harmony.tests.java.lang.ProcessManagerTest#testEnvironment"]
},
{
- description: "Crypto failures",
- result: EXEC_FAILED,
- names: ["libcore.javax.crypto.CipherTest#testCipher_ShortBlock_Failure",
- "libcore.javax.crypto.CipherTest#testCipher_Success"]
-},
-{
description: "Flake when running with libartd.so or interpreter",
result: EXEC_FAILED,
bug:22106064,